跳到正文

研究指南

股票回测与实验

A股回测卖出被拦:用T+1持仓日期重建可卖路径

A股回测拒绝卖出时,应先检查可卖库存和实际交易会话,再修改信号。退出条件成立,不代表刚买入的普通股票已经可以当日卖出。订单受阻后,现金、股数和费用都应保持原状,账户继续承担持仓风险,直到出现符合规则且确实提供的后续执行。

本文隔离一个普通A股单次建仓现金账户,跨越2026年国庆休市,核对同会话禁止出售和下一会话退出。它不是完整逐批次交收系统。CoTrading对T+1、证券例外、结算和实际券商约束的处理仍属规划或待核实。下面的独立诊断夹具使用合成价格,不冒充真实交易记录。

先确认交易日期适用的规则版本

上交所2026年交易规则英文译本规定交收前出售及特定品种当日回转的边界,不能把其他工具的例外直接套到普通现金股票。本例明确选择普通A股下一会话可卖的情形,不声称同一条简单规则覆盖交易所所有产品、账户和特殊安排。

上交所发布及生效通知写明2026修订自7月6日施行、2023版本同时废止,并列有实施安排。更早的历史回测应保留当时版本和品种特定规定。2026年10月阅读到的规则,不能未经核对就回填所有早期日期或全部板块;本文也没有声称完整实现通知附件的每个细项。

本夹具的相关状态是进入会话、当前会话、库存和现金,并假设建仓前没有旧股。这个前提非常关键:原有可卖股份与当天新购股份混合时,总持仓和可卖数量可能不同。单个entry_date不能表达多个取得批次各自的冻结或可卖状态,更不能仅凭总股数判断全部库存可退出。

下一交易会话不是下一自然日

上交所2026年国庆休市公告列明10月1日至7日休市,10月8日起照常开市。本例给定会话为9月30日、10月8日、10月9日。政府调休上班的周末也不能仅因有人工作就被当成交易所会话,交易日历需要独立依据。

9月30日买入以后,下一条给定市场会话是10月8日,不是10月1日。夹具通过有序合法会话表达T+1可卖边界,先检查请求日期是否存在,再核对进入与当前会话顺序。若简单把日期加一天,就可能在交易所关闭时制造卖出,导致后续现金与收益都不可执行。

长期系统应加载完整核实日历,而不是把这三个日期按工作日无限延伸。个股停牌和可执行bar是额外输入:交易所开放时某股仍可能不能成交,数据文件里恰好出现一个价格也不能让正式休市日成为可交易日期。日历、股票状态和行情质量必须分别保留。

先重现当会话卖出受阻

账户初始现金五千、零持仓。9月30日以10买入一百股,本金一千,费用一,现金降为3999,持仓一百,累计费用一,已实现价格损益零。账户保存本次唯一进入日期9月30日和进入价10,不把当前市场价覆盖成成本。

同日尝试以10.5卖出,返回T_plus_1_blocked。现金仍3999、库存仍一百、费用仍一、已实现价格损益仍零。拒单不是成交,不再收费,也不能把不存在的卖出款用于另一笔买入。若图表画出退出信号,而账本仍持有,应先核对这类资格原因,而非假定信号没有被读取。

如果错误地允许这次卖出,一百股价差会产生五十,扣两笔费用后看似净赚四十八。脚本只把四十八作为禁止路径的反例数字,从未把它记入账户或称为可以获得的收益。这能展示不合市场契约的订单,怎样把原本不能完成的机会变成诱人的模拟利润。

10月1日再次尝试卖出,返回not_a_session。这是休市拒绝,和同会话新股不可卖不同。两者都保持现金和库存,但修复原因不能混淆:改日历不能释放当日买入股份,改信号也不能让休市中的交易所开门。诊断记录应保留具体理由,不能统一压成一个模糊的“未成交”。

在下一会话真实记账并闭合现金桥

10月8日给定开盘9.5,进入会话在合法序列中更早,因此本夹具允许全卖一百股。卖出本金950,扣费用一,净收到949,与原现金3999相加得到4948。结束持仓零,累计费用二,不能仍按之前10.5的未执行请求计算利润。

已实现价格损益是一百乘9.5减10,等于负五十;净现金损益为负五十二。断言要求末现金减初始资本等于已实现价格损益减累计费用。这样才能把资格检查连接到实际亏损账户,而不是看到拒单标签正确就结束验收,也避免把毛价格损益和扣费结果混用。

从禁止的10.5机会到实际9.5开盘的下跌,是未退出暴露的后果。受阻退出不只是贴在已实现利润旁边的行政标记,账户仍拥有股票,后续价格会改变价值。样例提供了一个后续成交;如果下一开盘继续停牌、缺失或受其他规则阻止,还应有明确的待退出与估值政策,不能在本文范围内默认为已解决。

单次建仓模型应明确拒绝多批次

已有持仓时再次购买,会抛出additional_batch_unsupported,不覆盖原进入日、不合并批次,也不假装维护逐批冻结。覆盖日期可能把旧股误判为不可卖,或把新股误判为已经释放,取决于最后保留下来的是哪个日期。直接拒绝比静默选错其中一种更便于审阅。

本例只允许全额清仓,未实现部分出售、旧股加当日新购、公司行动、转入股份及各品种例外。完整扩展需要记录取得批次、总库存、可卖数量、每笔卖出的批次分配和结算状态,并为新规则准备独立样例。一个小类能够正确管理单次账户,不等于已经支持所有实际持仓结构。

本文也没有实现完整中国交易税费、印花税或涨跌停队列,一单位只是声明的合成费用。若要与真实券商对账,应替换成本模型并核对证券、账户与日期。T+1顺序正确,不能顺带证明其他撮合假设都已通过,更不能据此给出真实净收益或实际下单承诺。

运行输出并定位第一处状态差异

下载完整示例,使用Python 3.12或更新版本执行python R597.py,只依赖标准库。输入把真实冻结会话与合成订单价格结合,不连接交易所,不启动产品回测器。输出包括买入记录、受阻后的完整状态、休市尝试、下一会话成交以及最后现金桥。

断言同时检查精确拒绝标签、受阻前后状态不变、真实卖出内容、费用、空持仓和负五十二净损益。诊断实际系统时也应保存请求日、证券类别、生效规则版本、按会话取得数量、可卖库存、拒绝原因和最终成交。先找到第一处状态不一致,再决定修正哪一层,而不是为了让退出发生就放松市场约束。

拒绝记录也是库存证据的一部分

受阻卖出虽然不形成成交与费用,仍应保留诊断日志,把请求价10.5与后来执行价9.5分开。这样才能解释禁止路径的表面四十八利润为何没有进入最终负五十二账户。对比真实引擎时,先检查休市期间是否始终保留一百股;末值偶然相同,也可能隐藏提前卖出又错误买回的过程,不能只用最终现金判断规则实现。

若图表在9月30日画退出箭头,应确认它标的是请求还是成交,不能看到箭头就把库存改零。对应日志可以同时给出退出条件、可卖检查、拒绝状态和后续执行编号。持仓估值同样需要声明:保留股份不代表每天都存在可执行价格,更不能以缺价为理由立即实现此前的未执行利润。此例为隔离库存顺序,没有宣称假日期间连续估值或完整风险曲线。真实研究应把估值政策与卖出资格分开设计,发生缺失时保留明确状态,而不是用同一个价格字段兼任成本、标记价和实际成交价。

全额卖出后清空库存,仍保留原成交记录供对账,不把已实现价格损益重置为零。账户生命周期字段与当前仓位字段含义不同,后续再次进入应建立新交易记录;本例结束在第一次清仓,未把另一笔未测试的新交易计入结果。

常见问题

T+1是否就是下一自然日?

本例不是。可卖边界沿给定合法交易会话推进,国庆休市使9月30日之后的下一会话成为10月8日。

这个类能否管理旧股与新股混合批次?

不能,它明确拒绝追加批次。完整库存系统需表示各批次可卖状态,不能依赖单个进入日期。

相关阅读