策略仿真/实盘与策略回测中的差异问题以及解决方案

用户头像mindgoyyds
2023-06-09 发布

汇总一下目前实盘遇到比较多的问题、产生的原因以及如何解决问题。首先得从回测环境和实盘环境的一些区别开始说起

回测(模拟)与仿真柜台的区别?️

回测(模拟)环境和柜台环境(仿真、实盘)会有部分差异,如果在策略中不考虑这些差异并做对应处置,可能会导致策略在回测中正常运行,导致策略在仿真、实盘中出现bug。


梳理了一下目前主要有6个不同点(如有遗漏请补充):

回测 仿真
1️⃣ 一般没有初始持股 可能有初始持股
2️⃣ 委托通常会立刻成交 委托不会立刻成交
3️⃣ 持仓数据中有持仓天数position_days position_days一直等于0
4️⃣ 不存在策略外交易 策略外交易会影响策略内
5️⃣ 很少有撤单的场景 最好需要考虑撤单
6️⃣ 回报没有延迟 回报有延迟

可能导致的问题与解决方案?️

问题一:资金账号内有初始持仓

策略在回测/模拟中,在不设置的情况下是不会有初始持仓的,而在实际交易中,资金账号通常会有初始持仓。

  • 可能导致的问题:
    • 部分策略内部存在内部记录持仓信息的逻辑,在资金账号有初始持仓时,使用research_trade运行策略时,如果有加入同步函数sync_trade_api()或者策略的 signal_mode=False,策略内部无初始持仓的信息,可能导致冲突报错
  • 案例:策略库中的问财分钟模板

f565fca2a94777490155078155d6e4fd.png

在以上策略中,策略会将买入股票的信息记录在字典 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月底前)

总结

目前遇到的主要就是这些问题,如果在交易过程中有其他问题,也可以在本贴留言,最好可以提供测试代码复现问题便于我们查找原因。

评论

用户头像
2023-10-09 22:51:43

这些区别平台不能处理?如果中签的股票呢,一个账户只能用一个策略,还是我理解错误了!

评论
用户头像
2023-11-18 17:37:36

这些区别平台不能处理?如果中签的股票呢,一个账户只能用一个策略,还是我理解错误了!

评论

需要帮助?

试试AI小助手吧