Skip to content

第35章:回测系统的金融逻辑 ​

第34章已经说明多久调仓、怎样分批,以及买不进或卖不出时怎么办。现在把规则放回历史:某天盘后选中了乙,第二天乙涨了,回测就能记一笔收益吗?还不能。你得先知道订单何时进入市场、买到了多少,以及上涨发生时账户究竟持有什么。

回测是在当时的信息和交易条件下,逐步重建一个账户。 本章围绕三个问题展开:哪一段涨跌属于自己;成交如何改变现金和股份;分红、送股与停牌怎样进入净值。学完后,应能从一条净值曲线反查到具体账目。下一章才在这条可信曲线上计算和解释绩效,不讲回测程序的代码实现。

以下账户逻辑适用于个股和交易所上市 ETF;交易制度按各自品种和历史日期区分。所有金额、价格、费率与日期安排均为教学示意。

一、盘后选中的股票,不能替你赚到白天的涨幅 ​

假设你用周一完整收盘数据打分,周一收盘价10元,周二开盘11元。信号到周一盘后才完成,因此无法按周一10元买入。若周二确实以11元成交,周一收盘到周二开盘的10%上涨属于原来的持有人,不能算给刚买入的账户。

这里有四个必须分开的时点:信息已经拿到、信号算完、订单提交、订单成交。第27章处理了前两步的数据基础;本章还要保证后两步真的排在它们之后。只有公告日期、没有具体时刻的数据,不能默认开盘前已知;拿不准的记录应延后使用或排除,并披露这种处理。

一条可审查的日频约定可以是:“交易日 T 收盘后,用截至约定取数截止时刻已公开且已入库的数据生成目标,下一交易日提交订单。”若盘后公告晚于本次取数截止时刻,就要等下一次信号检查,不能因为它也标着 T 日便塞进本次排序。下一交易日由交易日历决定,不是日历简单加一天。

接着还要回答:次日什么时候成交?若选择开盘集合竞价,就必须让委托及时进入竞价;不能只因有开盘价,就假定自己的全部订单都能按这个价格成交。若选择开盘后再卖旧股、用到账资金买新股,新股就不能仍全部按开盘集合竞价价格成交。先卖后买是一条资金顺序,也是一条时间顺序。

下面用时间线把“看见上涨”和“赚到上涨”分开。沿箭头看,成交前的价格变化影响买入成本;成交后的价格变化才影响这笔新持仓的盈亏。

示意时间线:周一盘后形成信号,周二成交后才开始承担新持仓的涨跌

图中周二收盘也是11元,因此按11元买到的这笔股票,当天价格收益为零,扣费后反而略亏。若系统报出赚了10%,先查是否把成交前的隔夜上涨放进了新持仓收益,而不是立即解释为因子有效。

二、行情里有这个价格,不代表订单能在那里成交 ​

某只股票当天最低价10元,你挂了10元买单,日线也显示当天有成交。但低价可能出现在你报单之前,或者轮到该价格时前面的买单尚未成交完。行情记录的是市场发生过什么,回测还需要一套规则说明自己的订单能分到什么。

例如,预先安排在某个盘中时段买入,市场在这个时段的平均成交价是10元。自己的订单可能因为报单较慢、需要买的数量较多而买得稍贵,所以回测可假设按10.01元买入;卖出则可以作相反方向的不利调整。多出的每股0.01元,是对实际成交可能偏离参考价的估计,不是市场固定收取的费用。还要另行规定最多能成交多少,不能认为加了一点成本就一定能全部买到。这些数字只是示意,具体价格和数量怎样估计,留到第41章。

只用日线数据时,不能恢复盘中先后顺序和排队位置。可以事先采用一致的保守规则:停牌不成交;一字涨停买单、一字跌停卖单不假设成交;其他情况按声明的价格与数量模型处理。这是回测假设,不是对真实成交的证明。 若策略收益主要来自这些无法确认的交易,应补充更细数据,或比较更保守假设下是否仍有优势。

尤其不要用当日收盘后才知道的成交额决定当天开盘的订单金额。例如开盘前根据过去的成交情况决定买1,000股,这就是已经发出的订单。之后可以用实际交易时段的市场记录估计这张订单只成交了400股;却不能因为后来发现行情很好,就把原订单改写成买2,000股。前者在判断原订单能完成多少,后者是在事后修改当时的决定。用全天成交额估计开盘可成交量还会夸大开盘容量,两个时段不能混用。

买卖方向也不能省略。涨停时买入困难,不表示已有持仓必然卖不掉;跌停时卖出困难,不表示买入必然不行。真实限制要按当时价格、对手盘和证券规则判断。成交数量还受交易单位、可卖数量和资金约束,不能为了让目标权重恰好相等而允许小数股。

对无法成交的部分,沿用第34章事先约定的取消或限期重试规则,分别留下委托数量、成交数量、剩余数量和原因。取消订单只是撤回未完成意图;已成交的股份仍在账户里。停牌、跌停未卖出的旧股,也不能因它已经不在目标名单里就从账户消失。

三、先把一笔调仓的现金和股份算对 ​

目标要求卖甲买乙,但甲只卖出一部分。此时最危险的错误,是先把账户替换成目标持仓,再补算费用。这样做已经假设所有卖单提供了现金、所有买单都成功。正确顺序是:从真实旧持仓出发,每一笔确认成交才更新相应现金和股数。

先看一个可手算的例子。期初账户有现金10,000元、甲股票1,000股,每股10元,合计20,000元。甲全是以前买入的可卖股份。现在希望卖掉甲、买乙;当日甲只成交卖出400股,成交价10元,卖出费用共5元。随后买入乙500股,每股20元,买入费用共5元。这里假定乙买单在甲成交之后执行,且符合交易单位,两笔价格不表示同时拿到了开盘价。

卖甲实际收到4,000元,扣费后现金变成13,995元;买乙支出10,000元及5元费用,现金剩3,990元。甲剩600股,乙新增500股。为突出成交和费用,先假设估值价格仍分别为10元、20元。下面的表逐步核对这笔账,金额单位均为元。

账户状态(示意)现金甲股数/市值乙股数/市值账户净资产
交易前10,0001,000/10,0000/020,000
甲卖出400股并扣费13,995600/6,0000/019,995
乙买入500股并扣费3,990600/6,000500/10,00019,990

读表最有用的检查是:没有价格变化时,买卖只改变资产形态,总资产只减少实际费用10元。若最后仍是20,000元,费用漏记了;若甲已经为零,未成交的600股被错误删除了。若同时给乙记入目标所需的全部股份,现金可能被凭空透支。

为了把这条检查用于每个交易日,在没有出入金、分红和其他资金变动的交易段内,现金变化应满足:

Cafter=Cbefore+S−B−F

Cbefore、Cafter 分别为交易前后现金,S 是实际卖出成交金额,B 是实际买入成交金额,F 是这些成交产生的费用,全部以元计。上例就是 10000+4000−10000−10=3990。式子中的金额来自成交记录,不能来自目标金额或尚未成交的订单。

账户有现金、有股票,也未必现在就能用 ​

若尚未成交的买单冻结了3,000元,那么账面现金3,990元中,只有990元还能支持另一笔买入。冻结资金仍属于账户净资产,只是暂时不能重复使用;撤单确认后释放冻结资金,也不是赚了一笔收益。卖单占用的股份同样不能再提交第二次。

持有数量与可卖数量也要分开。上例甲剩600股可以按规则继续卖;乙当日新买500股,若为普通 A 股股票,受 T+1 限制,当日可卖数量为零。卖出证券所得资金通常可以当日用于证券交易,但“可交易使用”与“可转出到银行卡”是两回事。境内股票 ETF 通常也适用 T+1,允许日内回转的其他 ETF 则按具体产品规则处理。

因此,每次产生新订单,先核对已成交、有效未成交委托、可用现金和可卖数量,再检查费用、交易单位与组合限额。一个只做多且不借款的账户,不能依赖稍后可能成交的卖单来弥补当前买单的资金缺口。

四、分红和送股为什么不能被算成突然亏损或暴利 ​

持有1,000股,每股10元,市值10,000元。若每股派现0.50元,公司把这笔钱分给股东后,公司留下的资产也相应减少。因此交易所相应调整价格比较的起点,除息参考价降至9.50元,避免把分出去的钱误当成普通价格下跌。忽略市场波动和税费,你并没有突然亏掉500元:股票市值降为9,500元,同时取得500元红利权益。困难在于,权益确认、现金到账与可以再次买入,并不一定在同一天。

回测应先按股权登记日确定谁有权获得分红,再在除息日计入应收红利——已经属于投资者、但尚未到账的款项。到了实际到账日,把应收红利转成现金。不能等到账日才承认这笔资产,否则除息日会出现假亏损、到账日又出现假盈利;也不能提前当现金花掉,否则虚构了融资。

下面只跟踪这笔股票资产,假设三阶段没有市场波动、没有税费、没有再投资。用同一张表看权益从股票转到应收款再转到现金,便能区分“净资产没少”和“资金已经可用”。

阶段(示意)股票市值应收红利已到账红利现金合计
登记日收盘10,000元0010,000元
除息后、红利尚未到账9,500元500元010,000元
红利到账、价格不变9,500元0500元10,000元

实际价格不会被保证停在9.50元,市场涨跌应另算。个股现金分红还可能涉及按持有期限确定的红利税及后续补扣,回测需按投资者类型、历史规则和持仓批次处理;若简化为税前结果,应明确标注,不能声称已经得到个人投资者税后收益。

送股或转增则主要改变数量。假设每10股送转10股,原1,000股变为2,000股,理论除权价由10元降到5元,忽略其他因素总价值仍是10,000元。数量权益与除权价格要配套处理;新增股份在可交易日前应记录为待上市或受限权益,不能当作立即可卖的股份,也不能因为行情数量字段暂未更新而漏掉它们。配股还需要缴款,必须明确是否参与、从哪里扣现金,不能当作免费送股。

复权收益和分红入账,不能给同一笔钱记两次 ​

有些历史收益数据已经把股东拿到的红利算进回报,这也是使用复权数据时需要核对的内容。如果先用这样的收益增加账户,再额外把红利加进现金,同一笔分红就可能被计算两次。反过来,只用不复权价格计算涨跌,却遗漏分红和送转,收益又会偏低。

本章采用清楚的一套口径:用当时的不复权报价模拟成交和估值,用公司行动记录处理现金、股份和应收权益。 研究因子时可以另用口径核实过的复权序列;不能拿它充当真实下单价格。使用数据商提供的收益序列前,先查清它是否已计入红利、是否假设红利立即买回股票,不能把这种假设直接搬进现金尚未到账的账户。

对于 ETF,投资者账户收到的是基金对份额持有人的分配。底层公司向基金分红,已经影响基金资产与净值,不能再按成分权重给 ETF 持有人额外发一次现金。ETF 自身发生现金分红或份额折算时,再按基金公告的登记、除息、到账或可交易安排更新账户。

五、净值要按实际持仓算,不能按希望持有的比例算 ​

回到第三节的调仓,收盘时甲跌到9元,乙涨到21元。账户仍持有甲600股和乙500股,所以甲的下跌不会因“计划清仓”而消失,乙的上涨也只能按已买到的500股计算。收盘资产是现金3,990元、甲5,400元、乙10,500元,合计19,890元。

前面的普通交易日,只需把现金与股票市值相加。第四节的分红日则还要加上尚未到账的红利;如果已有确定要付但尚未扣除的费用,也要减掉,否则会高估自己拥有的资产。为了把这几种情况放进同一套账,可以在每天约定的时点计算净资产:

At=Ct+∑iqi,tPi,t+Rt−Lt

At 是时点 t 的账户净资产;Ct 是现金总额,包含仍属于账户的冻结现金;qi,t 是证券 i 已确认的持有数量,股票用股、ETF 用份;Pi,t 是同一时点每股或每份的估值价格。

Rt 是尚未计入前面项目的应收资产,如应收红利或单独记录的待上市股份权益;Lt 是尚未支付的应付费用、税款等负债。各项金额均为元。已经从现金扣除的费用不再放进 Lt,已计入股数的送转股份不再放进 Rt,以免重复。

本章无借款、无中途出入金,以期初净资产 A0 为基数,把起点设为1,则净值及每日收益为:

Vt=AtA0,rt=AtAt−1−1

Vt 是累计净值,rt 是从上一估值时点到本次的账户收益率,均无量纲。上例期初20,000元、收盘19,890元,因此净值0.9945,当日收益为−0.55%。还能拆账核对:甲亏600元、乙赚500元、费用10元,共亏110元,正好是20,000元的0.55%。

若中途追加10,000元,账户变大并不表示策略赚钱,以上直接相除就不再适用。此时须记录外部资金流的发生时点,采用份额法或按出入金时点分段计算收益。为专心审查交易过程,本章示例固定本金,不引入这层计算。

股票停牌了,今天的账户总值怎么算 ​

另看一个只用来说明停牌的简化账户:现金4,000元和甲股票600股。甲昨天收盘10元,账户合计10,000元。今天甲停牌,也就是暂停交易,因此没有今天的新成交价。但每天的收益仍要计算:现金还在,600股也还属于你,只是现在卖不出去。问题就在这里:股数知道,今天该用什么价格给它算市值?

一个简单回测可以事先约定:这类停牌期间暂用停牌前最后一个收盘价记账。于是今天仍按每股10元计算,股票记6,000元,加上现金,账户总值仍记10,000元。这里的“估值”就是给尚未卖出的资产确定一个记账金额;写6,000元,不等于已经收到6,000元现金,也不保证将来能卖出这个金额。

如果复牌——恢复交易——当天收盘变为9元,600股就只记5,400元,账户总值降到9,400元。下表只展示这一个持仓,假设期间没有买卖、分红或其他资金变化。顺着时间看,股数和现金都没动,改变的是用来记账的价格。

时点(示意)用来记账的每股价格仍持有的股数股票记账金额现金账户总值
停牌前收盘10元,当天收盘价6006,000元4,000元10,000元
停牌期间10元,暂沿用旧价6006,000元4,000元10,000元
复牌日收盘9元,当天收盘价6005,400元4,000元9,400元

停牌期间账上看似没涨没跌,是因为没有新交易价格,并不是公司经营和外部市场停止了变化。这个例子到复牌时才在账上表现出600元损失;它不能说明风险是复牌当天才产生的。若把停牌期间的平直曲线当成“这只股票很稳”,就会误判风险。

暂用旧价是一种简化假设。例如停牌期间公司已公告严重损失,就不能据此认定旧价仍合理。研究若另行估计价格,应说明当时的依据和计算办法;不能提前拿后来9元的复牌价回填停牌日。

实际检查一份回测时,先查当日是否确实停牌。如果只是数据漏了,就补查行情,不能把数据故障当停牌。确实停牌时,检查600股是否仍保留、记账价格从哪里来、现金是否无故增加。把股价填成0,会凭空记出6,000元损失;假装按旧价卖出,则会凭空多出6,000元可买其他股票的钱。这两种处理都没有对应的交易。

退市股票和终止运作、进入清算的 ETF 也要守住这一点:没有行情,不等于持有的权益消失或已经变成现金。只有实际卖出收到款项,或清算分配到账,才产生相应现金。拿不到后续资料时,应保留这笔未解决的持仓并说明结果受影响,不能按最后一个有报价的日子假装顺利退出。

持有 ETF 时,用哪个价格计算自己的盈亏 ​

假设买入一份 ETF 花了1.02元,当时基金公布的每份净值是1元。那1元描述基金资产折算到每份的价值,你实际付出的却是1.02元。若之后能以1元卖出,不计费用也会亏0.02元;直接拿基金净值当作买入价格,就漏掉了这笔损失。

因此,在交易所买卖 ETF 的回测,要用 ETF 的市场成交价格模拟买卖,通常用市场收盘价计算每日持仓金额。基金份额净值留给跟踪质量等分析,不能替换投资者实际面对的价格。

成本也要沿着“这笔钱在哪里扣了”检查。基金内部收取的管理费、托管费已经减少基金资产,体现在公布的净值里;用真实 ETF 市场价格做回测时,不要再从个人账户每日扣一次相同费用。自己买卖 ETF 的券商佣金等交易费用,仍要另外记账。

无论买卖个股还是 ETF,如果回测已经把10元参考价调整为10.01元,并按10.01元扣除买入款,多付的每股0.01元就已经计入成本。不能再以“滑点费”的名字扣一次。

六、回测说今天亏了多少钱,怎样检查它算得对不对 ​

第五节的例子里,我们算出账户从20,000元变为19,890元,亏110元。如果回测报告却写成19,900元、只亏100元,差的10元从哪里来?单看“今天跌了0.50%”很难发现原因;把这一天的买卖和费用逐笔列出来,才有办法定位。下面就用这个差额演示怎样检查。

先取出这一天回测留下的记录:开始时有多少现金和股票,何时发出什么订单,模型判定实际成交多少,扣了多少费用,以及收盘价格。这里检查的是回测模拟出的成交,不是说历史上自己真的下过这些单。 不能直接抄回测已经算好的日末总金额,要用这些原始记录重新算一遍。

第一步,核对订单是否在选股之后发出,再写下当天开始时的现金和股数。例如乙是前一天盘后选出的,买单就在之后提交;若记录却写着前一天已经买到,顺序已经不成立。第三节的账户开始有10,000元现金、甲1,000股,先把它们写下,不能直接从希望达到的目标持仓起算。

第二步,按成交先后算现金和股数。甲只卖出400股,收款4,000元、付费5元,所以现金应为13,995元,甲仍600股;随后乙买入500股,支出10,000元、付费5元,现金应为3,990元。若回测现金栏写4,000元,而成交金额和股数都与这些记录一致,就找到了那10元差额:两笔费用没有从现金扣掉。接下来应修正费用入账,再重新计算,而不是把这10元当作策略表现更好。

第三步,拿实际剩下的股数乘收盘价。甲600股乘9元为5,400元,乙500股乘21元为10,500元;加现金3,990元,总数19,890元。这样就知道110元损失来自哪里:甲亏600元,乙赚500元,再付10元费用。现金算错就回查收付款,股数算错就回查成交数量,价格用错就回查行情日期和价格口径。

不必手算每一个交易日,但只检查普通交易日也不够:一些错误只在卖不掉或分红时出现。下面列出适合优先抽查的情形,以及能当场判断对错的现象。它们整理的是前文已经讲过的账目关系。

抽查哪一种日子具体看哪里什么结果说明需要查错
有买卖并收取费用的日子成交金额、费用与现金变化是否一致费用有记录,现金却未扣除,如上例多出的10元
只卖掉部分股票或停牌的日子没卖掉的股份还在不在,是否被错误换成现金计划卖1,000股、只成交400股,账户却不再持有剩余600股
股票除息、红利尚未到账的日子股票降价的同时,是否记入了应收红利第四节的9,500元股票之外漏记500元应收款,出现假亏损;或提前用这500元买了股票

这就是把一天的账户重新算一遍的用途:找到收益数字背后具体哪一步有依据、哪一步不成立。算术对上以后,还要回到第一、二节检查信息当时是否可得、成交假设是否合理;账目能对上,是可信回测的必要条件,不能单独证明那些交易真的能做成。

本章小结 ​

  • 收益只能从持仓实际建立后开始计算;先卖后买既要资金够,也要时间顺序成立。
  • 订单是意图,成交决定现金与股数,公司行动另按权益和到账时点入账;冻结或尚不可卖不等于资产不存在。
  • 净值来自实际账户。先让每一天的涨跌、权益和费用对得上,再进入第36章判断策略表现好不好。