2016-06-17 7 views
3

DataFrame価格データを含む行ごとに、利益/損失額のDataFrameを計算しています。次のように売買戦略保有期間の損益計算書 - rolling_applyボトルネック解消

ロジックは次のとおりです。

  • 私たちは、現在の期間に資産を売却/購入します。
  • 資産はholding_periodのままです。
  • 保有期間中に価格がtake_profitを超える場合は、その価格で利益を得て終了します。
  • 保有期間中の価格がstop_lossを超える場合は、その価格で終了します。
  • 最初のtake_profitまたはstop_lossのレベルは、損益を終了するかどうかを決定します。
  • 利益または損失のいずれにも達していない場合は、保有期間の最後の価格で終了します。

私はこれを実装している方法は、DataFrameにおける各系列のローリング・ウィンドウ上提供関数を適用する、pandas.rolling_applyを使用することです。

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 

物事私が試してみました:

は、私が最初にtake_profitまたはstop_lossに到達したかどうかを判断するためにpandas.rolling_maxpandas.rolling_minを使用。

私はこのアプローチとしていた問題は2倍であった:

  • take_profitは非常によく、低価格で達している可能性があるためあなたは、take_profitのための出口価格として最大を使用することはできません。保持期間の最大値をリアルタイムで知ることは不可能です。
  • take_profitまたはstop_lossに最初に到達したものを特定することはできません。

質問:

は、各期間でP & Lを計算するためのより効率的な方法はありますか?

答えて

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を計算する - take_profit、cut_lossまたはperiod_endが発生した最初の日付を取得し、Pを計算& L(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% 
関連する問題