生产策略变更(Production Strategy Changes)

生产环境下的策略变更流程。

译文说明

注:本文为非官方翻译,可能存在疏漏;请以原文为准。

本文档说明在生产环境下如何调整策略:新增策略,或替换现有策略。

内容比较“骨架化(bare bones)”,在阅读本篇之前,你应该已经读过并理解生产环境文档

操作步骤总览

下面这些步骤必须按顺序执行:

  1. 配置所有新加入的品种,并开始采样价格数据
  2. 确认能跑通回测,并为生产环境创建对应的 .yaml 配置文件
  3. 创建用于运行该策略的自定义类
  4. 备份数据
  5. 停止所有相关进程
  6. 更新 private_config.yamlprivate_control_config.yaml
  7. 更新策略资本(strategy capital)
  8. 检查生产回测是否能正常运行
  9. 在策略之间转移头寸
  10. 跑完所有将要关闭或缩减资本的策略回测
  11. 确认仓位与交易限额合理
  12. 手工生成 instrument 订单
  13. 运行报告
  14. 重启进程
  15. 从配置文件中删除被替换掉的策略
  16. 做收尾清理

并非每一步都会在本文中详细展开,部分细节请参考生产环境文档

配置新加入的品种并开始采样价格

如果新策略中包含新的品种(instrument),需要把这些品种配置好,并让它们开始采样价格数据,详见/docs/data.md
只有拿到采样后的数据,你才能对策略做回测。

创建用于运行策略的自定义类

创建 run_system 类,用于运行策略回测

如果你使用的是完全“开箱即用(vanilla)”的标准策略,并且可以直接用默认的 stage 组合来运行,那么这一步可以跳过。
当前内置的标准策略包括:

如果不是上述情况,你需要自己实现一个 runSystem 类。
可以参考示例:/examples/production/example_of_custom_run_system.py

示例中通过重写 system_method 来构建一个不同于默认配置的系统,该系统由若干自定义 stage 组合而成。

如果你的策略在“保存优化后的头寸”方面与默认行为不同,还可能需要重写 run_backtest 方法(经典系统和动态优化系统之间的主要区别之一就在这里)。

创建订单生成类(order creation class)

同样,如果你使用的是完全默认的策略、且可以直接使用默认的订单生成逻辑,这一步也可以跳过。
当前默认的订单生成实现包括:

如果默认实现不适用,你需要自己写一个订单管理函数,并重写方法 get_required_orders

创建报表类(reporting class)

如果你打算沿用默认的报表逻辑,这一步同样可以跳过。
当前默认的策略报表实现包括:

如果默认报表不满足你的需求,你需要实现“报表函数”。
若你的策略与“经典系统”足够相似,可以像动态系统那样复用大部分代码。

更新配置文件

即便你是“替换”某个既有策略,我也强烈建议你一开始先把新策略追加到配置文件中,而暂时保留旧策略不动。

例如,在 private_control_config.yaml 中:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
process_configuration_methods:
  run_systems:
    ... existing systems ...
    dynamic_TF_carry:
      max_executions: 1
      object: private.systems.dynamic_carry_trend.run_system.runMySystemCarryTrendDynamic
      backtest_config_filename: private.systems.dynamic_carry_trend.production.yaml
  run_strategy_order_generator:
    ... existing systems ...
    dynamic_TF_carry:
      object: sysexecution.strategies.dynamic_optimised_positions.orderGeneratorForDynamicPositions
      max_executions: 1

注意这里:

  • object 字段引用了我们自定义或内置的 run_systemorder_generator 类;
  • backtest_config_filename 则指向生产环境使用的 .yaml 回测配置文件。

private_config.yaml 中,例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
strategy_list:
  ... existing systems ...
  dynamic_TF_carry:
    load_backtests:
      object: private.systems.dynamic_carry_trend.run_system.runMySystemCarryTrendDynamic
      function: system_method
    reporting_code:
      function: sysproduction.strategy_code.report_system_dynamic_optimised.report_system_dynamic
strategy_capital_allocation:
  function: sysproduction.strategy_code.strategy_allocation.weighted_strategy_allocation
  strategy_weights:
    dynamic_TF_carry: 99.99
    medium_speed_TF_carry: 0.01

思路是:

  • 让新策略与旧策略并行存在一段时间,以便对比与验收;
  • 在确认一切无误后,再把旧策略从这些配置中彻底删掉。

备份数据

在对生产系统做重大变更之前,务必对数据做一次完整备份。
如何备份取决于你的具体部署方式,例如:

  • 备份 MongoDB / Postgres / 其它数据库;
  • 备份 CSV / Parquet 等文件;
  • 备份 private_* 配置等。

这一步没有详细统一流程,但强烈建议不要跳过。

停止所有相关进程

在修改配置、切换策略之前,先停掉所有相关的生产进程。
可以通过:

  • interactive_controls
  • 或者相关的系统启动 / 停止脚本;
  • 或者你自己使用的进程管理工具(如 systemd、supervisor 等)

来完成这一步。

更新 private_config.yamlprivate_control_config.yaml

在停掉进程并做好备份以后:

  • 把新策略加入 private_config.yamlprivate_control_config.yaml
  • 暂时不要删掉旧策略相关配置;
  • 确保:
    • 每个新策略在 strategy_list 中都有条目;
    • 运行策略回测、生成订单、运行报表等进程都能在 process_configuration_methods 中找到对应条目。

更新策略资本(strategy capital)

当你新增或替换策略后,需要更新各个策略的资本配置,以便:

  • 新策略能拿到合适的资本;
  • 被替换或缩减的旧策略资本相应减少;
  • 总资本分配保持一致。

可以使用现有的 update_strategy_capital 流程,具体见 production_zh.md 中的“按策略分配资本”部分。

在初始验证阶段,你也可以先给新策略分配一个较小的名义资本,只为验证整体流程是否工作正常。

检查生产回测是否能正常运行

使用 update_system_backtests 脚本,检查:

  • 新策略的生产配置能否正常跑通回测;
  • 是否会生成合理的“最优头寸(optimal positions)”。

如果此处就报错,说明配置或实现仍有问题,应优先修复。

在策略之间转移头寸

如果你是“完全或部分替换”某个策略,那么最好把头寸在策略之间迁移,否则:

  • 旧策略会先平掉原有仓位;
  • 新策略再重新开出几乎相同的仓位;
  • 结果就是多了一轮没必要的交易成本。

仓位转移通过一个脚本完成:它会按当前市价生成“伪 instrument 订单(pseudo instrument orders)”。
示例(Python):

1
2
from sysinit.futures.strategy_transfer import *
transfer_positions_between_strategies('medium_speed_TF_carry', 'dynamic_TF_carry')

这里会把 medium_speed_TF_carry 策略中的头寸迁移到 dynamic_TF_carry

跑完所有将要关闭或缩减资本的策略回测

这一步属于“收尾清理”:

  • 对那些资本将被缩减或清零的策略,重新运行一次策略回测;
  • 让这些策略的“最优头寸”根据新资本自动调整;
  • 避免出现“position break”(实盘头寸与理论头寸不一致);
  • 同时也防止旧策略因为资本配置变了而产生多余订单。

确认仓位与交易限额合理

通过 interactive_controls 脚本:

  • 为新策略设置策略级的仓位 / 交易限额
  • 或者调整现有限额,使之适应新的组合。

特别是在新增高杠杆或高波动策略时,这一步非常关键。

手工生成 instrument 订单

建议先运行 update_strategy_orders 脚本:

  • 既可以对新策略运行,也可以对旧策略运行;
  • 如果你替换了旧策略,并且已经把头寸迁移过来,那么旧策略的最优头寸应为 0;
  • 在这种情况下,旧策略不应该再生成任何真实交易。

同时,可以运行 interactive_order_stack,查看:

  • 当前各策略 / 品种的头寸;
  • instrument 订单栈是否符合预期。

运行报告

建议运行完整的报表流程,以确认一切工作正常,并检查:

  • 策略报告是否如预期般反映了新策略行为;
  • 组合层面的风险、收益、成本是否合理。

尤其是新策略对应的“策略报告(strategy report)”,在这一阶段非常有参考价值。

重启进程

在上述检查都通过后,可以重新启动生产进程。

重启之后,注意确认:

  • 新增的品种是否真的可以交易(如无额外监管或技术限制);
  • 是否不存在权限、合约限制、交易所规则等方面的意外问题。

清理工作

当前这一节在原文中是 FIXME,尚未给出详细步骤。
一般来说,你需要:

  • 在确认新策略稳定运行后:
    • private_config.yamlprivate_control_config.yaml 中删除已被替代的旧策略;
    • 清理不再使用的回测配置 .yaml
    • 按需清理不再使用的数据与代码(谨慎操作,避免误删)。

实现具体策略

本节简单说明如何实现文中提到的几类“预制策略”。

Vanilla classic strategy

原文此处仍为 FIX ME,即尚未补完,目前没有额外说明。

Dynamic optimisation strategy(动态优化策略)

这是这篇博文中描述的那套“最小跟踪误差 + 动态优化”的策略。

忽略的品种(ignore instruments)

.yaml 配置中的 ignore_instruments 字段下,我建议只填入因合约乘数不同而“重复”的品种,例如:

  • SP500SP500_micro 中,只保留一个;
  • 其他完全等价的“mini / micro / full-size”对也类似处理。

不要在这里填入“不可交易品种”。
这样一来,在计算“标的权重(instrument weights)”时,仍会包含那些暂时不可交易的品种。

设置 shadow cost

shadow_cost 是一个关键参数,在 private_config.yaml 中设置(不是在回测配置文件中设置,因为它在策略订单生成阶段、即回测外部使用)。

  • 默认值为 50
  • 在刚上线新策略时,你可能希望先用一个非常大的值(例如 500),然后在最初几天里逐步降低;
  • 这样可以让“从旧策略头寸过渡到新策略头寸”的过程更加平滑。

需要特别注意的是:

  • 如果某个策略头寸的方向完全错误(sign 错误),那么无论 shadow_cost 设为多少,该头寸都会被立即平掉,除非你将该品种设置为“不交易(don’t trade)”。

策略回测输出的“最优头寸”

动态优化策略回测输出的“最优头寸”会带有一个特殊的 -raw 后缀,这意味着:

  • 默认情况下它们不会被直接展示;
  • 但会在策略报表中显示出来。

确认仓位与交易限额合理

对动态优化策略来说,仓位限额尤为关键,因为优化器会直接使用这些限额。
建议使用 interactive_controls 脚本中的“自动填充(auto‑populate)”功能来初始化这些限额,再按需要微调。

确保“不要交易 / 只减仓”标记已设置

对你不打算交易的品种,必须设置:

  • don't trade(不要交易);或者
  • reduce only(只减仓,不加仓)

这些标记在 interactive_controls 中设置。
常见原因包括:

  • 成本过高(详见成本报告);
  • 合约成交量太小(参见流动性报告);
  • 风险贡献太小(也是流动性报告中体现);
  • 监管限制(例如美国 Reg 871,或英国对非 MiFID 专业客户禁止某些 crypto 衍生品);
  • 无法获得或过于昂贵的 tick 数据(如果你使用其他数据源获取日度数据)。

如果某品种当前没有任何仓位,那么设置 don't tradereduce only 的效果是一样的。

实际的最优头寸

真正参与订单生成的“实际最优头寸”是一些简单的整数值
此外,它们还包含了大量关于优化过程的诊断信息,这些信息会显示在:

  • 策略报告中;
  • 以及 interactive_diagnostics 中用来查看“最优头寸”的输出里。

这些诊断信息对于理解优化行为、排查问题非常有帮助。