跳到正文

研究指南

股票回测与实验

改了股票策略代码结果为何变了:找出首次信号分歧

修改股票策略代码后,最终回测结果不同,并不能说明哪个分支导致变化。先固定数据、生效配置和执行行为,再按会话与证券定位第一个不同信号。接着追目标仓位、实际成交和后续账户路径,不能把后来每个差异都当成另一个新代码缺陷。

本文用同一合成执行器比较两个冻结策略函数,属于确定性的代码版本实验,与让模型重新生成一份策略分开。价格虚构,CoTrading是否已有源码级比较能力仍为规划或待核实。真实对照每次保持一个市场与币种,不混合账户或行情环境。

让代码成为唯一有意改变的输入

保存两份源码,并记录共同数据、复权政策、证券身份、参数、引擎、依赖、费用、日历和初始账户。比较实际消耗产物,而不是只看文件名或界面标签。价格快照或下单数量改变,就不满足纯代码归因前提,需要先把混合变化分开。

样例共享价格[100,100,101,99]、初始现金一千、目标一股和下一价格执行规则,只更换策略函数。控制检查器在解释前拒绝数据、配置或引擎记录变化。它不能发现完全未记录的依赖,因此真实程序还可能需要补充隐藏输入观察。

生成代码与执行也要分开。运行期间若再次调用外部模型,应先保存其输出并冻结,再开始比较。此题研究两个已知程序,随机生成或修改指令属于另一实验,不能仍说只有一行源码变化,也不能把模型输出分布的问题混入确定程序对照。

阅读源码差异,但不先猜账户结果

版本一返回current >= previous,版本二返回current > previous,即从正信号分支去掉等号。文本差异指出候选原因,但必须在相同观测上实际执行,才能知道第一次影响发生在哪里。不是所有价格路径都会经过等号分支。

Python difflib官方文档说明统一格式文本差异。样例读取实际函数源码,并断言补丁包含比较符变化。这是源代码证据,不是自动解释绩效因果关系的工具;后续状态链仍由运行结果核对。

def signal_v1(current, previous):
    return current >= previous

def signal_v2(current, previous):
    return current > previous

同时检查默认值、单位、导入和副作用等其他修改。大型重构变化很多时,可拆成更小受控比较。不能只从补丁挑一句听起来合理的解释,却忽略实际首次分歧在另一个位置;也不能只看最终余额恰好相同,就断言全部行为未变。

按稳定机会键对齐信号

采用会话与证券身份,必要时再加同一时点的稳定序号,对两边键的并集连接。某版本独有一行,表示另一边缺记录,不自动等于零或平仓。错误地补零,可能把输出故障伪装成一个看似合法的策略决定。

样例证券为S,信号会话为一和二,以字典键比较,反向插入顺序不会造成假变化。简单按行zip可能把不同证券配对,或在一边较短时静默截断。先核对覆盖再比较数值,避免只有恰好能配上的行进入结果。

需要规范化时,保留原时区和信息时间。随意舍入或排序可能消除执行相关先后。目标是识别同一次决策机会,而不是不断加工文件,直到两份输出看起来相同。多个股票同日出现信号时,证券身份尤其不能被行号替代。

定位第一次分支影响

会话一当前与前价都为一百。版本一发一,版本二发零,因此首分歧为会话一、证券S、等号分支。版本一目标一股,版本二目标零股。这是有具体输入支持的分支定位,不是仅凭源码中存在等号就推断。

该决策时权益一千,目标权重分别零点一与零。权重来自目标股数乘当时已知价格,不使用未来成交价。目标股数与目标权重要分开:一股目标不是每天固定百分之十,价格和账户权益变化后,权重也会变化。

会话二价格一百零一高于前价一百,两个函数都发一,目标股数再次一致。但不能因此说账户已经重新相同,较早信号已经改变持股和现金,后续执行必须从各自真实状态继续。

追踪持仓路径到后续成交

共同执行政策是在下一合成价格成交。版本一在会话二以一百零一买一股;版本二当时仍空仓,在会话三以九十九买入,响应会话二相同的正信号。后一个买入价应直接从输入索引三读取,不能把信号价当成交价。

阶段版本一版本二
会话一信号10
会话一目标权重0.10.0
首次买入会话2,101会话3,99
期末估值权益9981000

版本一在较晚共同目标出现时已经有一股,不再加买;版本二需要买入才能达到同样目标。会话三成交不同,是首次分歧的持仓路径后果,不一定意味着策略分支又出现第二次独立变化。应明确区分目标相同与执行差额相同。

两者最后都持一股,按九十九估值。版本一现金八百九十九,权益九百九十八;版本二现金九百零一,权益一千。费用零,价格流用作简化可执行标记,仅检验局部账户桥,不验证真实流动性、手数或成交保障。

区分传播效应与新分歧

保存代码分支、输入窗、信号、目标、成交和账户状态的连接表。发现首分歧后,独立比较后来信号与依赖账户的委托。信号相同、仓位不同造成的下单差异,应标为传播,不应反复计成新的逻辑错误,夸大版本变更范围。

有些策略直接把账户状态作为信号输入,后续信号变化也可能是反馈。可以设计受控重放,让两函数接收相同账户快照,区分直接代码效应和反馈。但那是另一个明确实验,不能覆盖两条原始账户路径,或者把真实持仓硬改一致后仍称原运行。

代码修改的成功条件也不是永远要求最终权益相等。有意改变行为时,应检查首次分歧是否符合预期分支,后续能否勾稽;行为保持重构则需要在声明政策下输出等价。验收标准取决于修改意图,不能看结果好坏后临时切换。

反例:顺序、缺行与混合干预

第一项反例反转信号行插入顺序,按键比较仍为相等。第二项删除会话二记录,要求返回missing_record,不补零。这些检查保护对齐方法,与等号改动是不同机制。没有它们,比较工具本身就可能制造不存在的源码问题。

第三项把控制记录的目标数量由一改二,程序拒绝纯代码比较。如果不检查,较大订单可能被错归因于比较符变化。必须先解决配置差异,或另立混合实验,不能一边承认参数变了,一边仍给出单一源码根因结论。

完整例还断言首个目标权重、准确成交清单和期末余额,并从真实函数提取文本,而不是手写一份补丁就假装代码已经运行。这些是针对合成分支的检查,没有覆盖生产策略可能出现的全部路径,未触达分支仍需要与修改风险相称的验证。

建立可复核定位表

交接时每条差异至少附会话、证券、两边原始输入、两边信号、目标和紧邻成交引用。首个变化能追到源码分支,后续变化注明是新逻辑还是状态传播。对象行顺序变化可以在表示层归一化,但缺失事件必须进入异常表,不能为了表格整齐被过滤。

若两次输出连证券清单都不一致,先确认是不是代码有意改变覆盖范围,再决定如何解释首个缺行。未覆盖、未触发和输出失败是不同状态。只有了解实际接口含义,才能正确判定;比较器不能凭一条缺行猜出确切业务原因。

运行与更新条件

下载完整示例。使用Python 3.12或更新版本,运行python R616.py。标准库程序报告会话一/S/等号分支、权重零点一和零、在一百零一与九十九买入、期末九百九十八与一千,并明确缺行状态、拒绝混合配置。

归档两份源码、共同控制引用、对齐键、首输入窗与信号到账户链,说明变化是否有意、怎样验收。后续传播差异即使已经解释,也保留记录,不因为找到根分支就删除。检查者需要看到完整影响范围,而不仅是一行修复说明。

数据、配置、引擎或任一版本再变化时重新建立比较,保留旧包,区分新干预与解释修订。本文证明固定合成条件下的源码到信号追踪,不提供真实历史盈利证据,也没有验证CoTrading已有自动调试器。真实接入应对隐藏依赖和执行约束继续核实。

常见问题

后来每笔不同成交都是新代码错误吗?

不是。早期信号改变持仓和现金,后来信号相同仍可能产生不同委托。

信号记录缺失可以补零吗?

不能。缺输出与明确空仓决策需要分别调查。

相关阅读