汇总一下目前实盘遇到比较多的问题、产生的原因以及如何解决问题。首先得从回测环境和实盘环境的一些区别开始说起
回测(模拟)与仿真柜台的区别?️
回测(模拟)环境和柜台环境(仿真、实盘)会有部分差异,如果在策略中不考虑这些差异并做对应处置,可能会导致策略在回测中正常运行,导致策略在仿真、实盘中出现bug。
梳理了一下目前主要有6个不同点(如有遗漏请补充):
回测 | 仿真 | |
---|---|---|
1️⃣ | 一般没有初始持股 | 可能有初始持股 |
2️⃣ | 委托通常会立刻成交 | 委托不会立刻成交 |
3️⃣ | 持仓数据中有持仓天数position_days | position_days一直等于0 |
4️⃣ | 不存在策略外交易 | 策略外交易会影响策略内 |
5️⃣ | 很少有撤单的场景 | 最好需要考虑撤单 |
6️⃣ | 回报没有延迟 | 回报有延迟 |
可能导致的问题与解决方案?️
问题一:资金账号内有初始持仓
策略在回测/模拟中,在不设置的情况下是不会有初始持仓的,而在实际交易中,资金账号通常会有初始持仓。
- 可能导致的问题:
- 部分策略内部存在内部记录持仓信息的逻辑,在资金账号有初始持仓时,使用research_trade运行策略时,如果有加入同步函数sync_trade_api()或者策略的 signal_mode=False,策略内部无初始持仓的信息,可能导致冲突报错
- 案例:策略库中的问财分钟模板
在以上策略中,策略会将买入股票的信息记录在字典 context.information
中。当资金账户有初始持仓时,卖出这部分股票的时候,策略会将 context.information
中的持仓信息删除,但由于 context.information
字典没有这部分股票代码的key(因为不在策略内买入,没有记录),使用del方法删除持仓信息时导致报错:
- 解决方案(用户侧,具体方案需视策略实际情况而定):
- 初始化时在
context.information
中记录初始持仓信息 - 策略运行时不要在策略外部进行交易
- 使用dict.pop()删除字典中的key,而不是del方法
- 初始化时在
- 长期解决方案(SuperMind功能优化):
- 以策略为单位构建资产单元(类似分仓功能),将策略内外部资金、持仓、委托、成交数据隔离(预计7月-8月)
问题二:实盘中委托通常不会立即成交
- 可能导致的问题:
- 策略内部存在内部记录持仓信息的逻辑,在委托后立即记录买入,实际上委托后不会立刻成交(等待时间视委托价格和当前行情走势而定)。此时就有可能导致信息被漏记/多记
- 案例:示例代码
import time
def init(context):
g.information = {}
g.symbols = ['000001.SZ','600519.SH']
def handle_bar(context):
for symbol in g.symbols:
order(symbol,100)
for symbol in list(context.portfolio.positions):
g.information[symbol] = 1
time.sleep(3)
for symbol in list(context.portfolio.positions):
print(g.infomation[symbol])
以上代码在回测中通常不会出现问题,因为撮合是在策略内部串行且市价单通常可以成交。但是在实盘中,股票下单后汇报和撮合不会像回测中那样进行,订单的撮合和策略时同步进行的,此时,按示例代码的方式,就可能会由于订单未成交,导致信息未被记录进g.information
,而在三秒之后订单成交,再使用持仓代码去读g.information
的数据时,导致策略出错。
- 解决方案(用户侧,具体方案需视策略实际情况而定):
- 优化记录持仓信息的代码,比如在收盘后根据持仓、成交等信息统一进行计算,减少漏记、多记的发生
- 考虑使用dict.get()函数获取字典内的数据
- 长期解决方案(SuperMind功能优化):
- 增加成交回报事件、委托状态更新事件推送(计划6月底前)
问题三:券商/仿真柜台返回的持仓数据中没有position_days数据
- 可能导致的问题:
- 策略内使用此数据来进行控制最大持仓天数部分代码可能失效
- 案例:示例代码
from datetime import timedelta as td
def init(context):
g.symbols = ['000001.SZ','600519.SH']
g.status = True
def handle_bar(context):
if g.status:
for symbol in g.symbols:
order(symbol,100)
g.status = False
else:
for k,v in context.portfolio.positions:
trade_days = get_datetime() - td(v.positions_days)
tdays = len(get_trade_days(
trade_days.strftime('%Y%m%d'),
get_datetime().strftime('%Y%m%d')
))
if tdays>5:
order_target(k,0)
此示例代码希望实现的是买入股票池后持有5天后卖出,在回测中没有问题,但是在仿真/实盘中,由于柜台没有positions_days的数据,因此v.positions_days一直会等于0,导致此部分代码无法实现预期效果。
-
解决方案(用户侧,具体方案需视策略实际情况而定):
- 增加记录持股天数的数据,但需要注意避免问题一和问题二
-
长期解决方案(SuperMind功能优化):
- 以策略为单位构建资产单元(类似分仓功能),将策略内外部资金、持仓、委托、成交数据隔离(预计7-8月),并根据此计算出position_days数据
问题四:策略外交易会影响策略内
-
可能导致的问题:
- 手动买入的持仓被策略卖出,手动卖出的持仓被策略买回
- 策略代码报错
-
解决方案(用户侧,具体方案需视策略实际情况而定):
- 处理起来比较麻烦,改动比较多,代码能弱的同学暂时尽可能不要在策略外做手动交易
-
长期解决方案(SuperMind功能优化):
- 以策略为单位构建资产单元(类似分仓功能),将策略内外部资金、持仓、委托、成交数据隔离(预计7-8月)
问题五/问题六:实盘中需要考虑更复杂的场景
-
策略回测及模拟交易时,策略可以说实在相对静态的环境下运行的,并且时不考虑延迟的。而在实际的交易中,市场瞬息万变,无论是数据获取、计算耗时这种,还是下单与回报的延迟(不可控),都会导致实盘中产生更复杂的情景
-
可能导致的问题:
-
考虑订单长期未成交情况下的处理方式,回测中对手价/市价单通常可以立刻成交,而实际交易中则相对来说有较大概率不会立刻成交
- 例如:价格变动剧烈时,下单后未成交,又未及时撤单、追单,降低资金效率。可能会影响策略调仓,造成策略表现变差
-
在策略逻辑上充分考虑到订单生成、下单到券商柜台、券商柜台回报所产生的延迟,避免策略出现异常
- 例如:在下单后立刻撤销订单,此订单刚生成,未完成初始化,处于不可撤销的状态,从而导致报错
-
尽可能减少在handle_bar中获取任何数据,提高计算效率,避免策略本身产生较高的延时
- 例如:handle_bar在9.31分被触发,从触发到下单中间计算耗时五分钟,在回测中,订单仍然会以9.31分的行情数据进行撮合。而在实盘中,订单会在9.36分被发出,从而产生延时成本。
-
-
解决方案(用户侧,具体方案需视策略实际情况而定):
- 考虑复杂场景,并增加相对于的策略代码
-
解决方案(SuperMind功能优化):
- 增加成交回报事件、委托状态更新事件推送(计划6月底前)
总结
目前遇到的主要就是这些问题,如果在交易过程中有其他问题,也可以在本贴留言,最好可以提供测试代码复现问题便于我们查找原因。