基于Python Pandas实现多仓位止盈止损回测向量化优化
海量行情数据回测:向量化实现多仓位止盈止损出场计算
我用Python Pandas处理本地700万+行的市场数据做策略回测,入场信号已实现向量化且效率达标。现在需要解决多仓位的止盈止损出场计算问题:每个仓位有独立的止盈(take profit)和止损(stop loss)价格阈值,需为每个开仓记录匹配对应的出场价格和时间。
现有数据结构
带Datetime索引的DataFrame如下:
import pandas as pd from pandas import Timestamp import numpy as np df = pd.DataFrame({ 'open': {Timestamp('2021-01-03 22:11:00'): 1.22319, Timestamp('2021-01-03 22:12:00'): 1.22315, Timestamp('2021-01-03 22:15:00'): 1.22324, Timestamp('2021-01-03 22:16:00'): 1.22355, Timestamp('2021-01-03 22:17:00'): 1.22357}, 'high': {Timestamp('2021-01-03 22:11:00'): 1.22319, Timestamp('2021-01-03 22:12:00'): 1.22318, Timestamp('2021-01-03 22:15:00'): 1.22358, Timestamp('2021-01-03 22:16:00'): 1.2236, Timestamp('2021-01-03 22:17:00'): 1.22361}, 'low': {Timestamp('2021-01-03 22:11:00'): 1.22317, Timestamp('2021-01-03 22:12:00'): 1.22315, Timestamp('2021-01-03 22:15:00'): 1.22324, Timestamp('2021-01-03 22:16:00'): 1.22352, Timestamp('2021-01-03 22:17:00'): 1.22355}, 'close': {Timestamp('2021-01-03 22:11:00'): 1.22317, Timestamp('2021-01-03 22:12:00'): 1.22315, Timestamp('2021-01-03 22:15:00'): 1.22358, Timestamp('2021-01-03 22:16:00'): 1.22352, Timestamp('2021-01-03 22:17:00'): 1.22356}, 'longEntrySignal': {Timestamp('2021-01-03 22:11:00'): False, Timestamp('2021-01-03 22:12:00'): False, Timestamp('2021-01-03 22:15:00'): True, Timestamp('2021-01-03 22:16:00'): False, Timestamp('2021-01-03 22:17:00'): False}, 'longEntry': {Timestamp('2021-01-03 22:11:00'): False, Timestamp('2021-01-03 22:12:00'): False, Timestamp('2021-01-03 22:15:00'): False, Timestamp('2021-01-03 22:16:00'): True, Timestamp('2021-01-03 22:17:00'): False}, 'longEntryPrice': {Timestamp('2021-01-03 22:11:00'): np.nan, Timestamp('2021-01-03 22:12:00'): np.nan, Timestamp('2021-01-03 22:15:00'): np.nan, Timestamp('2021-01-03 22:16:00'): 1.22355, Timestamp('2021-01-03 22:17:00'): np.nan}, 'longTpPrice': {Timestamp('2021-01-03 22:11:00'): np.nan, Timestamp('2021-01-03 22:12:00'): np.nan, Timestamp('2021-01-03 22:15:00'): np.nan, Timestamp('2021-01-03 22:16:00'): 1.2243451663854852, Timestamp('2021-01-03 22:17:00'): np.nan}, 'longSlPrice': {Timestamp('2021-01-03 22:11:00'): np.nan, Timestamp('2021-01-03 22:12:00'): np.nan, Timestamp('2021-01-03 22:15:00'): np.nan, Timestamp('2021-01-03 22:16:00'): 1.2227548336145146, Timestamp('2021-01-03 22:17:00'): np.nan}}) print(df)
输出:
open high low close longEntrySignal longEntry longEntryPrice longTpPrice longSlPrice 2021-01-03 22:11:00 1.22319 1.22319 1.22317 1.22317 False False NaN NaN NaN 2021-01-03 22:12:00 1.22315 1.22318 1.22315 1.22315 False False NaN NaN NaN 2021-01-03 22:15:00 1.22324 1.22358 1.22324 1.22358 True False NaN NaN NaN 2021-01-03 22:16:00 1.22355 1.22360 1.22352 1.22352 False True 1.22355 1.224345 1.222755 2021-01-03 22:17:00 1.22357 1.22361 1.22355 1.22356 False False NaN NaN NaN
字段说明:
longEntrySignal:标记下一根K线的开多信号(True/False)longEntry:标记当前K线为开仓K线(True/False)longEntryPrice:开仓K线的开盘价,作为开仓成本longTpPrice/longSlPrice:对应仓位的止盈/止损价格阈值
期望输出
新增exitPrice和exitTime列,记录每个仓位的出场信息:
open high low close longEntrySignal longEntry longEntryPrice longTpPrice longSlPrice exitPrice exitTime 2021-01-03 22:11:00 1.22319 1.22319 1.22317 1.22317 False False NaN NaN NaN NaN NaN 2021-01-03 22:12:00 1.22315 1.22318 1.22315 1.22315 False False NaN NaN NaN NaN NaN 2021-01-03 22:15:00 1.22324 1.22358 1.22324 1.22358 True False NaN NaN NaN NaN NaN 2021-01-03 22:16:00 1.22355 1.22360 1.22352 1.22352 False True 1.22355 1.224345 1.222755 1.224345 2021-01-03 22:29:00 2021-01-03 22:17:00 1.22357 1.22361 1.22355 1.22356 False False NaN NaN NaN NaN NaN
规则说明:
exitPrice:触发止盈则取longTpPrice,触发止损则取longSlPrice;若同一K线同时触发两者,优先取止损价- `exitTime``:触发止盈/止损的K线时间
当前方案与问题
目前筛选开仓行后用apply()调用自定义函数getLongExit计算出场信息:
entryDf = df[df['longEntry']].copy() entryDf[['exitPrice', 'exitTime']] = entryDf.apply(lambda x: getLongExit(exitDf=df[['high', 'low']], entryPrice=x['longEntryPrice'], entryTime=x.index, takeProfit=x['longTpPrice'], stopLoss=x['longSlPrice']), axis=1, result_type='expand')
getLongExit内部通过.loc、.idxmax()和.idxmin()判断止盈止损的触发顺序,返回对应结果
存在的问题
- 效率低下:
apply()逐行处理,面对700万+行数据时速度极慢 - Numba方案报错:尝试用Numba加速时出现类型错误,错误信息如下:
Traceback (most recent call last): File "/Users/maxwitt/PycharmProjects/ForexStrategies/strategy1.py", line 288, in <module> get_long_exit( File "/Users/maxwitt/PycharmProjects/ForexStrategies/venv/lib/python3.10/site-packages/numba/core/dispatcher.py", line 468, in _compile_for_args error_rewrite(e, 'typing') File "/Users/maxwitt/PycharmProjects/ForexStrategies/venv/lib/python3.10/site-packages/numba/core/dispatcher.py", line 409, in error_rewrite raise e.with_traceback(None) numba.core.errors.TypingError: Failed in nopython mode pipeline (step: nopython frontend) No implementation of function Function(<built-in function setitem>) found for signature: >>> setitem(array(float64, 1d, C), int64, datetime64[ns]) There are 16 candidate implementations: - Of which 16 did not match due to: Overload of function 'setitem': File: <numerous>: Line N/A. With argument(s): '(array(float64, 1d, C), int64, datetime64[ns])': No match. During: typing of setitem at /Users/maxwitt/PycharmProjects/ForexStrategies/strategy1.py (167) File "strategy1.py", line 167: def get_long_exit(index, high_vals, low_vals, tp_prices, sl_prices, out_exit_price, out_indices): <source elided> out_exit_price[idx1] = sl_entry out_indices[idx1] = index[idx2] ^
寻求解决方案
需要高效的向量化实现方案,解决海量数据下的出场计算效率问题,同时修复Numba类型错误。
内容的提问来源于stack exchange,提问作者snky
相关产品推荐
相关产品推荐

