交易策略持有期的盈亏计算 – 解决滚动应用瓶颈

我正在计算一个包含价格数据的DataFrame中每行的盈亏金额的DataFrame

逻辑如下:

  • 我们在当前时间段买入/卖出资产。
  • 我们持有资产holding_period的时间。
  • 如果在持有期内,价格超过take_profit,则以该价格退出并盈利。
  • 如果在持有期内,价格超过stop_loss,则以该价格退出并亏损。
  • 在持有期内首次达到的take_profitstop_loss水平决定我们是盈利还是亏损退出。
  • 如果既未达到止盈也未达到止损,则在持有期的最后一个价格退出。

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

考虑到rolling_apply会对DataFrame中的每一行和每一列组合调用函数,这是一个严重的瓶颈。

我想知道是否有更好的方法使用其他pandas/numpy功能来实现这个目标?

这是当前的实现:

def potential_pnl(prices, side, periods, take_profit=np.nan, stop_loss=np.nan):    # 根据买入/卖出所需的价格变动方向设置符号    if side == Side.SELL:        take_profit *= -1    else:        stop_loss *= -1    def period_potential_pnl(window):        # 以第一个价格进入,窗口的其余部分是可能的退出价格        entry_price = window[0]        exit_prices = window[1:]        take_profit_price = entry_price + take_profit        stop_loss_price   = entry_price + stop_loss        # 计算一个布尔数组,显示达到止盈/止损的位置        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 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    # 将`period_potential_pnl`应用到数据框上    pnl = pd.rolling_apply(prices, periods + 1, period_potential_pnl)    # 向后移动periods个周期,使退出盈亏与进入价格对齐    pnl = pnl.shift(-periods)[:-periods]    return pnl

我尝试过的方法:

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

这种方法的问题有两个方面:

  • 你不能使用最大值作为take_profit的退出价格,因为take_profit很可能在更低的价格就已经达到;在实时情况下无法知道持有期的最大值会是多少。
  • 你无法确定take_profitstop_loss哪个先达到。

问题:

是否有更有效的方法来计算每个周期的盈亏?


回答:

这是一种处理方法:

from datetime import datetime, timedeltafrom dateutil.relativedelta import relativedeltafrom pandas_datareader.data import DataReader

样本数据:

prices = DataReader('IBM', 'yahoo', datetime(2015, 1, 1), datetime.today().utcnow())['Open'].resample('D').fillna(method='ffill')prices.head()Date2015-01-02    161.3099982015-01-03    161.3099982015-01-04    161.3099982015-01-05    161.2700042015-01-06    159.669998Freq: 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)

Related Posts

L1-L2正则化的不同系数

我想对网络的权重同时应用L1和L2正则化。然而,我找不…

使用scikit-learn的无监督方法将列表分类成不同组别,有没有办法?

我有一系列实例,每个实例都有一份列表,代表它所遵循的不…

f1_score metric in lightgbm

我想使用自定义指标f1_score来训练一个lgb模型…

通过相关系数矩阵进行特征选择

我在测试不同的算法时,如逻辑回归、高斯朴素贝叶斯、随机…

可以将机器学习库用于流式输入和输出吗?

已关闭。此问题需要更加聚焦。目前不接受回答。 想要改进…

在TensorFlow中,queue.dequeue_up_to()方法的用途是什么?

我对这个方法感到非常困惑,特别是当我发现这个令人费解的…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注