股票趋势跟随回测案例:固定均线规则与次日开盘成交
股票趋势策略需要持续持有状态、完整热身窗口,以及晚于信号收盘的成交时点。本例固定一百二十个交易观察的简单均线:完整收盘严格高于均线时目标做多,否则目标现金;订单最早使用下一条指定交易日开盘。每项规则先冻结,再用能手算的数据验证。
研究包使用一只虚构美股和美元现金账户,价格路径包含亏损,也包含缺开盘的不同机制反例。它不是历史策略成绩,不声称稳定盈利,也不证明CoTrading已提供策略模板;相关产品能力仍为规划或待核实。数字只用于核对入场、连续持有、退出和现金。
固定规则与价格输入
本例指标使用完整日级原始收盘,成交使用单独给定开盘,合成区间没有公司行动。真实股票研究必须统一复权、现金分红和持仓变化处理,不能混用拆股调整指标与没有解释的原始数量。股票池应有历史成员和资格日期,当前成分列表并不能证明过去成员。
窗口包含当前收盘,共一百二十条。只有一百一十九条时目标为零,恰好满足才计算。严格大于为做多,相等或低于为零,所以平价序列不入场。它是每日状态,连续符合代表继续持有,不是每天新增一个完整仓位。若要只在穿越时触发一次,必须另写事件规则。
账户只有一个方向,买整数股,每笔实际订单支付虚构佣金一,基线不另加滑点。空仓且前一完整收盘目标做多时建仓;已有股且前一目标为零时退出;做多状态不变时不每日重调数量。每日重平衡会产生不同换手和成本,不能在实现时随意替换。
第一百二十条收盘怎么形成信号
前一百一十九条收盘均为一百,后面四条是,一百零二、一百零四、九十、八十九。索引从零开始,行一百一十九首次拥有完整窗口,均值为一百加二除以一百二十,一百零二高于它。行一百二十也符合;行一百二十一的九十跌破其尾部均线,请求退出。
从行一百一十九开始的信号尾部为[1, 1, 0, 0],之前全零。首次符合时,当天开盘已经过去,因此不能在这个模型中用之前的一百,或同一收盘一百零二来买入。每笔成交保存对应决策行,才能直接看出是否误用了已经发生的价格。
给定开盘在行一百一十九之前均为一百,行一百二十为一百零三,行一百二十一为一百零五,行一百二十二为八十八。退出信号收盘九十到可执行开盘八十八的跳空,应进入账户结果。收盘触发均线退出,不会把卖价锁定在九十,不能把信号参考价当保证成交价。
基线账户逐笔复算
初始现金一千,持仓零。行一百二十看到前一收盘符合,预留佣金一后,按一百零三计算最大可买整数股为九。名义金额九百二十七,加佣金一,剩现金七十二。行一百二十一前一信号仍为一,维持九股,没有额外订单,也没有再付一次佣金。
行一百二十二读取前一收盘零信号,以八十八卖九股,名义收入七百九十二,减佣金一,最终现金八百六十三,没有剩余股票。总亏损一百三十七,其中价格差损失一百三十五,佣金二。这个人为价格路径故意不追求漂亮收益,目的是检验从信号到现金的全链路。
Investor.gov官方订单说明区分了观察到的成交价格和保证执行价格。这里假定按给定开盘完成,真实历史模型还要解释流动性、滑点和限制。几条给定价格足以验证算术,不足以证明某种账户规模可以在真实市场按该价全部成交。
缺开盘改变的是执行路径
删除行一百二十的可用开盘,但保留完整收盘信号。系统不能在缺失开盘买入,也不能替换成上一收盘。本例采用持续目标重试规则:到行一百二十一,前一收盘仍为做多,因此按一百零五买九股,支付佣金一。缺失的那一天要记录未成交事件。
退出请求仍在行一百二十二到达,九股按八十八卖出,再付佣金一,最终现金八百四十五。这个反例改变的是执行数据可用性,均线计算没有改变。若系统只输出一次上穿事件,可能不会重试而始终空仓;那是另一个规格,不能悄悄替代本例的持续状态。
基线八百六十三与延后八百四十五相差十八,来自九股入场每股贵二,并不是指标错误。保存缺失日、重试原因和实际后续订单,才能区分数据缺失、重复位移与窗口变化。仅从最终曲线看差异,无法确定是哪条机制导致,诊断应回到逐笔证据。
运行完整窗口样例
文件为docs/growth/research/round-5/examples/R583.py,实际计算一百二十条窗口,没有为方便演示缩成三日。核心信号如下:
signals = [
int(i >= 119 and close > sum(closes[i - 119:i + 1]) / 120)
for i, close in enumerate(closes)
]
运行python docs/growth/research/round-5/examples/R583.py。断言检查一百一十九个热身零、精确信号尾部、基线成交索引一百二十与一百二十二、九股数量、最终八百六十三,以及缺开盘后在一百二十一和一百二十二成交、最终八百四十五。反例单独重放账户,不会只删掉买单却保留虚假卖单。
换成历史数据前补齐研究包
冻结代码表、交易日历、初始资金、指标价格处理、开盘来源、窗口、比较符、执行延迟、数量、费用和缺行情规则。保存配置快照和订单流水,再按现金加持仓估值复算每日权益。研究结束时是强制清仓还是保留估值持仓,也应写清,不能混用两种末期口径。
如果改成A股,应另外建模可卖条件与价格限制,不沿用美股合成样例中不受限制的成交。交易制度需要核对市场来源和历史版本。真实历史结果还应报告拒单、换手、暴露和成本,并与预先选定、同日期同估值口径的基线比较。数据、股票池或执行规则更新后,先重跑可手算边界,再解释新业绩表。
对于未成交目标,还要明确下一日是否继续尝试、何时取消,以及新信号是否覆盖旧目标。本例持续状态使重试自然发生,但实际系统可能有订单有效期和部分成交。若这些信息缺失,应把研究范围写成“固定开盘完全成交的机制演示”,不要用复杂实盘词汇填补没有实现的环节。只有输入、规则、订单和现金能由独立读者逐项重建,策略复现包才达到这里的验收要求。
下载完整示例,保存为R583.py,使用Python 3.12及以上运行python R583.py。仅使用标准库与合成数据。
在两个信号之间也核对账户状态
每行保存前一目标、开盘是否可用、动作前数量、订单原因、费用、动作后现金和收盘估值。这样基线路径的行一百二十一能清楚显示:目标为一且已有九股,因此没有第二次购买;另一条路径则是目标同样为一、但还没有股,所以允许延后建仓。只看信号表,无法区分这两个账户状态,也无法解释为何出现不同订单。
每次动作后先核数量,再算收益。缺行情阻止买入时,之后的退出信号不能卖不存在的股;退出若被阻止,股票仍应留在账户并继续估值,不能因目标归零就宣称账户已经空仓。卖出阻止属于本例缺入场之外的新分支,实际加入前应保持未验证状态,不把文字描述当成已经实现。每扩展一个状态转移,就补一个同时检查信号和现金的样例。
固定一条事件排序也能避免隐藏差异:先读取已完成前一收盘目标,再判断当前开盘是否存在,再按已有数量决定动作,最后更新现金和持仓。若改成先用当前收盘重新判断,再回到当日开盘成交,就产生另一条信息路径。审阅时把这一顺序逐项对照代码,比只检查是否写了“下一日”更准确;保留最后一行无后续开盘的未执行目标,避免为追求完整退出而补造价格。
常见问题
为什么不用只在上穿时触发的一次事件?
持续信号每天定义目标,也能明确缺开盘后的重试;一次事件属于另一种规则。
是否证明一百二十日最好?
没有。窗口由本例规格固定,没有进行参数搜索或真实市场比较。