【问题标题】:Trading strategy P&L for holding period - solve rolling_apply bottleneck持有期交易策略盈亏——解决rolling_apply瓶颈
【发布时间】:2016-10-19 08:54:14
【问题描述】:

我正在为包含价格数据的DataFrame 中的每一行计算DataFrame 的损益金额。

逻辑如下:

  • 我们在当前时间段买入/卖出资产。
  • 我们持有 holding_period 的资产。
  • 如果在持有期间价格超过take_profit,则以该价格退出获利。
  • 如果在持有期间,价格超过stop_loss,则以该价格退出亏损。
  • 看到的第一个take_profitstop_loss 级别决定了我们是盈利还是亏损退出。
  • 如果既没有达到止盈,也没有达到止损,则在持有期最后一个价位退出。

我实现它的方式是使用pandas.rolling_apply,它将提供的函数应用DataFrame 中每个系列的滚动窗口。

鉴于rolling_applyDataFrame中的每一行列组合调用一个函数,这是一个严重的瓶颈。

我想知道是否有更好的方法可以使用其他 pandas/numpy 功能来实现这一点?

这是当前的实现:

def potential_pnl(prices, side, periods, take_profit=np.nan, stop_loss=np.nan):

    # set sign depending on direction of price movement required by BUY/SELL
    if side == Side.SELL:
        take_profit *= -1
    else:
        stop_loss *= -1

    def period_potential_pnl(window):
        # enter at the first price, rest of the window are possible exit prices
        entry_price = window[0]
        exit_prices = window[1:]

        take_profit_price = entry_price + take_profit
        stop_loss_price   = entry_price + stop_loss

        # calculate array of bools showing where take_profit/stop_loss is reached
        if side == Side.BUY:
            filtered = exit_prices[ (exit_prices >= take_profit_price) |
                                    (exit_prices <= stop_loss_price) ]
        else:
            filtered = exit_prices[ (exit_prices <= take_profit_price) |
                                    (exit_prices >= stop_loss_price) ]

        # if neither take_profit/stop_loss is reached, exit at the last price
        # otherwise exit at the first price which exceeds take_profit/stop_loss
        if len(filtered) == 0:
            exit_price = exit_prices[-1]
        else:
            exit_price = filtered[0]

        exit_pnl = exit_price - entry_price
        if side == Side.SELL:
            exit_pnl *= -1
        return exit_pnl

    # apply `period_potential_pnl` onto the dataframe
    pnl = pd.rolling_apply(prices, periods + 1, period_potential_pnl)

    # shift back by periods so the exit pnl is lined up with the entry price
    pnl = pnl.shift(-periods)[:-periods]
    return pnl

我尝试过的事情:

我最初使用pandas.rolling_maxpandas.rolling_min 来确定是否到达take_profitstop_loss

我在使用这种方法时遇到的问题有两个:

  • 您不能将最高价格用作take_profit 的退出价格,因为take_profit 很可能以较低的价格达到;无法实时知道持有期的最长期限。
  • 您无法确定首先到达take_profitstop_loss 中的哪一个。

问题:

有没有更有效的方法来计算每个时期的损益?

【问题讨论】:

    标签: python numpy pandas machine-learning


    【解决方案1】:

    这是解决此问题的一种方法:

    from datetime import datetime, timedelta
    from dateutil.relativedelta import relativedelta
    from pandas_datareader.data import DataReader
    

    样本数据:

    prices = DataReader('IBM', 'yahoo', datetime(2015, 1, 1), datetime.today().utcnow())['Open'].resample('D').fillna(method='ffill')
    prices.head()
    
    Date
    2015-01-02    161.309998
    2015-01-03    161.309998
    2015-01-04    161.309998
    2015-01-05    161.270004
    2015-01-06    159.669998
    Freq: D, Name: Open, dtype: float64
    

    计算pnl的函数 - 获取第一个发生止盈、止损或周期结束的日期,并使用相应的退出价格计算盈亏(对于sell策略,反向profit_goalcut_loss

    def get_pnl(prices, start_date, holding_period=90, profit_goal=0.10, cut_loss=.10):
        end_date = start_date + timedelta(days=holding_period)
        data = prices[start_date: end_date]
    
        start_price = data.iloc[0]
        take_profit = start_price * (1 + profit_goal)
        cut_loss = start_price * (1 - cut_loss)
        exit_date = end_date
    
        if (data > take_profit).any():
            exit_date = data[data > take_profit].index[0]
        if (data[:exit_date] < cut_loss).any():
            exit_date = data[data < cut_loss].index[0]
    
        exit_price = data.loc[exit_date]
        print('Entered on {0} at: {1:.2f}, exited on {2} at {3:.2f} for {4:.2f}%'.format(start_date.strftime('%Y-%b-%d'), start_price, exit_date.strftime('%Y-%b-%d'), exit_price, (exit_price/start_price-1)*100))
    

    并试运行:

    for start_date in [datetime(2015, 1, 1) + relativedelta(months=i) for i in range(12)]:
        get_pnl(prices, start_date)
    

    得到:

    Entered on 2015-Jan-01 at 161.31, exited on 2015-Apr-01 at 160.23 for -0.67%
    Entered on 2015-Feb-01 at 153.91, exited on 2015-Apr-24 at 170.23 for 10.60%
    Entered on 2015-Mar-01 at 160.87, exited on 2015-May-30 at 171.35 for 6.51%
    Entered on 2015-Apr-01 at 160.23, exited on 2015-Jun-30 at 163.99 for 2.35%
    Entered on 2015-May-01 at 173.20, exited on 2015-Jul-30 at 160.50 for -7.33%
    Entered on 2015-Jun-01 at 170.21, exited on 2015-Aug-20 at 152.74 for -10.26%
    Entered on 2015-Jul-01 at 163.97, exited on 2015-Aug-24 at 143.47 for -12.50%
    Entered on 2015-Aug-01 at 161.40, exited on 2015-Aug-24 at 143.47 for -11.11%
    Entered on 2015-Sep-01 at 144.91, exited on 2015-Nov-30 at 138.61 for -4.35%
    Entered on 2015-Oct-01 at 145.31, exited on 2015-Dec-30 at 139.58 for -3.94%
    Entered on 2015-Nov-01 at 140.44, exited on 2016-Jan-20 at 118.46 for -15.65%
    Entered on 2015-Dec-01 at 139.58, exited on 2016-Jan-20 at 118.46 for -15.13%
    

    【讨论】:

      猜你喜欢
      • 2016-10-02
      • 2015-07-14
      • 1970-01-01
      • 2021-02-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多