Ste*_*mer 5 python numpy machine-learning pandas
我正在计算包含价格数据DataFrame
中每一行的损益金额DataFrame
。
逻辑如下:
holding_period
。take_profit
,则以该价格退出以获取利润。stop_loss
,则以该价格退出,造成损失。take_profit
或stop_loss
看到的水平决定了我们是盈利还是亏损退出。我实现此目的的方法是使用pandas.rolling_apply
,它将提供的函数应用到 中每个系列的滚动窗口上DataFrame
。
鉴于rolling_apply
为 中的每个行和列组合调用一个函数DataFrame
,这是一个严重的瓶颈。
我想知道是否有更好的方法使用其他 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
Run Code Online (Sandbox Code Playgroud)
我尝试过的事情:
我最初使用pandas.rolling_max
andpandas.rolling_min
来确定是否达到take_profit
or 。stop_loss
我使用这种方法时遇到的问题有两个:
take_profit
,因为take_profit
很可能已经以较低的价格达到;不可能实时知道最长持有期限是多少。take_profit
或中的哪一个。stop_loss
问题:
有没有更有效的方法来计算每个期间的损益?
这是解决此问题的一种方法:
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
from pandas_datareader.data import DataReader
Run Code Online (Sandbox Code Playgroud)
样本数据:
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
Run Code Online (Sandbox Code Playgroud)
计算函数pnl
- 获取 take_profit、cut_loss 或 period_end 发生的第一个日期,并使用相应的退出价格计算损益(对于策略profit_goal
则相反:cut_loss
sell
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))
Run Code Online (Sandbox Code Playgroud)
并测试运行:
for start_date in [datetime(2015, 1, 1) + relativedelta(months=i) for i in range(12)]:
get_pnl(prices, start_date)
Run Code Online (Sandbox Code Playgroud)
要得到:
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%
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
2388 次 |
最近记录: |