HoppyQuant
EN

第 2 课

买卖规则有了,为什么还不能直接回测?

补齐资金占用、候选冲突、交易成本、滑点和期末估值,让账户实验拥有明确执行约定。

蛙兄把“第十根 K 线收盘退出”保存好,又看了一眼上一课的结果表。

“那我拿 100 点资金,照这个办法买卖,最后能赚多少?”

“这张表还回答不了。”蛙博士说。

“买的时候算过了,卖的时候也算过了,还差什么?”

蛙博士没有马上回答,而是在纸上写了个小例子。

周一,第一个买入机会来了。蛙兄把钱买了进去。

周二,又来了一个机会。

“第二个,你拿什么买?”

“等第一个卖掉?”

“可第一个还没到退出时间。”

蛙兄看了看纸上的两个机会,又看了看自己那一笔钱。

原来,表格能把每次机会都算进去,账户里的钱却不能同时出现在两个地方。

以前算的是每次机会,现在算的是这笔钱

之前,我们把一次次九拍事件单独拿出来:假设在约定时点买入,再按规则卖出,这一次赚了还是亏了?

算完所有事件,再看平均收益、中位数、正收益比例。

这样做很有用。它帮助我们检查九拍之后的表现,比较买入筛选器,也比较了不同的卖法。

但它没有检查另一件事:这些买卖,能不能由同一个账户全部完成?

同一天可能有好几个信号。前一笔还没退出,后一笔可能又来了。我们在事件表里都能算一遍,在账户里却得看手上还有没有钱。

所以,前面的平均收益不能直接拿来回答“我的 100 点资金会变成多少”。它不是这笔钱一路买卖之后的结果。

这次,我们把问题换成:

本章重点

如果只有一笔有限的钱,按已经确定的买卖规则一路走下去,最后会剩多少?

为了回答它,我们需要模拟一个账户:什么时候拿着现金,什么时候持有股票,什么时候收回资金,再怎样参与下一次机会。

把这些事情按历史日期顺序走一遍,就是接下来要做的账户回测。

事件研究把重叠机会分别计算;同一个账户只有一笔资金,持有第一笔交易时不能重复使用这笔钱。
图 1|事件可以重叠计算,同一账户却不能重复使用已经占用的资金。

先不把 100 点拆成好几份

“那把钱分成几份,不就能多买几家了?”蛙兄问。

可以,那会是另一套资金使用办法。

不过,这次我们先用最简单的一版:资金不拆分,一次只持有一家公司的股票。

账户从 100 点现金开始,没有股票,也不借钱。

空仓时,如果出现符合规则的买入机会,就用可用现金参与这一笔,给买入费用留出空间。持有期间,不买其他公司,也不继续加钱买同一家公司。

等到按规则卖出,扣掉相应成本,再用剩下的钱等待下一次机会。没有机会的时候,就拿着现金。

注意,是用剩下的钱,不是每次重新发 100 点。

举个只为说明资金变化的例子:假设一笔交易结清以后,100 点变成了 105 点,那么下一轮就是从 105 点继续。如果只剩 95 点,就从 95 点继续。账户不能亏了以后悄悄重置。

也不能一边持有股票,一边把它的账面价值当成另一笔现金,再买一次。

这 100 点只是方便观察变化的起点,不是现实中的 100 元。我们暂时不把不同市场的买卖单位、最低收费等细节全部搬进来,也不设计多只股票之间怎样分配资金。

这是一套简化的教学实验条件,不是在建议你现实中把全部资金押在一只股票上。

同一笔资金从现金到买入、持有、卖出扣成本,再用剩余现金继续;不会在每笔交易前重置为100。
图 2|同一笔资金在现金、持有、卖出与再次买入之间循环。

钱不够的时候,不能等涨完再选

一次只买一家,事情简单多了,但仍然有几个小问题。

比如,账户空着,同一天却有三个符合条件的机会。

“那买后来涨得最多的那个?”

蛙兄说完,自己先笑了。

那是看过后面的数据才知道的,不是买入之前能作出的选择。

我们需要一个提前说好的办法。例如,按股票代码固定排序,选排在最前面的一个。这个办法不代表代码靠前的公司更好,只是遇到多个机会时,程序有一个明确的处理顺序。

选哪一种简单办法可以讨论,但不能先跑出三条收益曲线,再装作自己一开始就知道该选谁。

还有一种情况:钱已经用在第一笔交易里,第二个信号来了。

按照这次不拆资金的约定,就跳过它。不是先欠着,等有钱以后再按几天前的价格补买。

如果想让错过的机会排队,等以后再买,那就改变了我们已经固定的买入时点,也成了另一个需要研究的方案。

卖出与再次买入的先后也得说清楚。课程参考卖法是在第十根有效日 K 收盘退出,那么当天早上,这笔卖出的钱显然还没回来,不能拿去参与当天早上的买入。

这些约定不用变成一堆要背的知识。你只要能跟着一笔钱想明白:这一刻,它到底还是股票,还是已经可以使用的现金?

买卖,还会花掉一点钱

前面的事件研究,主要比较买入价和卖出价。账户回测还要给买卖留一点成本。

先看两件最容易理解的事。

一件是交易费用。 买卖可能需要付费,这部分钱付出以后,就不再属于账户。买入时也要算上它,不能把现金全部用来买股票,再凭空多出一笔钱付费用。

另一件是成交价的偏差。 图上的参考价格,不一定就是你实际能买到或卖掉的价格。我们可以在模拟时作一个简单假设:买入价比参考价稍高一点,卖出价稍低一点。这种成交价格与参考价格之间的偏差,通常叫作滑点。

本次不会研究怎样把成本压到最低,只会先约定一组用于教学的数值,让 AI 在每笔买卖里按同一套办法计算。

数值是什么、买卖两边怎样算,都要写下来。不能结果不好看时把成本删掉,也不能把教学里设的费用说成所有市场、所有账户的真实收费。

这里先知道成本会从账户里扣走就够了。到底扣了多少、影响有多大,等账户实际跑出来再看。

为什么这次要和沪深300比?

蛙兄又想起一个问题。

“之前不是和随机日期比吗?怎么这次换对手了?”

因为我们现在问的不是同一个问题。

和随机日期比较时,我们想知道:九拍完成这个时点,是否比普通日期更特别?

现在,我们已经有了买卖规则,准备把有限的钱放进去模拟。除了看自己赚没赚钱,还想知道:做了这些选择、等待和买卖,相比同期市场参照,表现怎么样?

因此,我们把沪深300作为这一轮统一使用的比较基准。这里的“基准”,你可以先理解成放在旁边的一把尺子。

看一个假设例子,不是我们的回测结果:

账户从 100 点变成 105 点,赚了 5%。如果同一时期沪深300上涨了 15%,那么账户虽然赚钱,表现却落后了 10 个百分点。

反过来,假设账户亏了 5%,同期指数跌了 10%。账户比指数少跌,但自己的钱仍然变少了。

“自己赚没赚钱”和“有没有超过参照对象”,是两个问题,两个都值得看。

作图时,我们可以让两条线都从 100 起步,使用同一个起止区间。一条线跟着账户的实际变化走;另一条线按沪深300相对起点的涨跌变化走。

不是拿股票账户的 100 点,直接和指数原本几千点的数值比高低。

指数那条线也不是我们真的下单买到了一个“指数账户”。它只是比较用的参照,我们需要把使用的数据字段和收益口径一起说明白。

选择沪深300,是为了让这轮研究有一个统一的市场参照,不是说它适合所有策略。我们的参考路线只选了一个教学行业,和它的构成并不相同;即使最后跑赢,也不能只凭这一条曲线就断定是九拍的功劳。

随机日期用来检查九拍时点是否更特别,沪深300用来比较有限资金账户与同期市场参照的表现。
图 3|随机日期检查事件时点,市场基准比较账户的同期表现。

如果蛙兄晚几天开始呢?

蛙兄盯着开头那个小例子,又冒出一个问题。

“如果我周一没开始,周二才拿着钱进来,是不是就能买第二个机会了?”

有可能。

早一天开始的人,钱可能已经用在第一笔交易上;晚一天开始的人,手里还拿着现金。虽然买卖规则一样,后面能参与的机会却可能不同。

这也提醒我们:只看一个账户,可能会把它恰好赶上的机会,当成整套规则通常会有的表现。

所以,这次除了看一个账户怎样运行,还加一个小检查:让十个账户错开时间出发,看看结果差得多不多。

你可以把它想成十个人,每个人各拿 100 点资金,遵守同一套买卖和费用规则,只是开始的日期不同。各自的钱各自用,不互相借钱,也不合起来算成一个大账户。

不是把 100 点拆成十份,也不是一个人同时持有十只股票。每个账户仍然一次只买一家。

出发安排先写好:第一个账户从研究期第一个市场交易日开始,第二个晚两个市场交易日,第三个再晚两个,以此类推,共十个。它们都在同一个研究期末结束。

这里的“开始”,是开始等待符合规则的机会,不是当天必须买一只。九拍仍然根据当时已经发生的历史行情计算,不会因为新账户刚启动,就把股票之前的计数清零。

两个交易日的间隔只是这轮教学检查的约定,不是我们找到了最佳出发节奏。不能看过结果以后,把日期挪到最好看的地方。

也别急着把它当成十次独立验证。十个账户经历的大部分行情相同,可能买到很多相同的交易。如果错开的这几天根本没有买入机会,它们甚至可能在同一天买入第一笔,走出完全一样的结果。这时也照实记录。之后可以另开一轮、扩大出发日期的范围,但要先写好新的安排,保留原结果,不能覆盖旧记录,也不要一直挪日期,直到得到喜欢的曲线。

它回答的是一个更小、也很实际的问题:在这组起点下,账户表现是否特别依赖出发的那一天? 它不能把所有运气消掉,也不能代替后面那段留出数据的检查。

下一课,我们先看最早启动的账户怎样走完一笔笔交易,再用一张简单图,把十个账户的结果放在一起。除了中位数,也看看最好与最差差了多少,以及有几个超过了各自同期的沪深300。不只报一个平均数,更不只展示最赚钱的那一个。

由于开始日期不同,每个账户都要配上自己起止区间内的指数参照,不能统一拿最早账户那段指数收益来比较。看十个账户的差异时,也要记得它们经历的时间长度略有不同。

这些汇总是在描述十种出发安排的差异。即使十个里面有八个跑赢,也不是在说“未来有八成概率跑赢”。

让 AI 把这些话整理成账户规则卡

到这里,我们不是又多找了一个指标,而是把“怎么用这笔钱”补进了原来的买卖规则。

打开研究项目,让 Codex 读一读上一课保存的交接单。使用 WorkBuddy 也可以。如果换了新对话,告诉它文件在哪里;它不需要知道课程叫什么,但需要读到你实际确定的规则。

你可以这样开始:

交给 AI 研究助手

请先读取当前项目的研究交接单,复述已经确定的买入与退出规则。找不到必要记录时,请让我提供,不要重新选策略,也不要替我猜。

我想为接下来的历史账户回测准备一张规则卡:初始现金100点,不追加资金、不借钱,资金不拆分,一次只持有一家公司的股票。空仓时,用可用现金按已定规则买入,预留交易费用;持有期间不加仓,也不参与其他信号,不把错过的信号留到以后补买。卖出后扣除成本,用剩余资金继续等待机会;没有机会时持有现金。

请帮我检查还有哪些影响执行的选择没说清楚,例如同一天多个机会怎样选、卖出的钱何时可用于下一次买入,以及费用和滑点怎样设。用简单例子解释,并为尚未确定的事项提出一个容易执行的教学方案,让我确认,不要自行标成已经冻结。已有明确约定就沿用,不重复问。

准备在2021—2022年运行账户,并与同一区间的沪深300比较,两条曲线都从100起步。请一并梳理期末仍有持仓、缺少行情、指数数据口径和以后验证期账户怎样起步的处理办法。数据不支持的真实交易限制,请列为教学简化,不要编造。尤其不要把之前为了比较卖法而筛选出的“完整二十根事件”名单,直接当成账户可以买入的名单。

再把错峰起步检查写进同一张规则卡:共十个互不调拨资金的模拟账户,每个初始现金100点、空仓,买卖与资金成本规则完全相同。以研究期第一个市场交易日为起点,分别偏移0、2、4、6、8、10、12、14、16、18个市场交易日启动,统一在研究期末结束。第一个固定为主账户,不按收益选主账户。启动后才允许按原定买入时点参与事件,不强制启动日买入,不补买过去错过的事件;计算九拍和指标仍使用决策时点之前已有的历史数据,不在账户启动日重置。

每个账户与各自起止区间的沪深300比较,请在规则卡里写清对应日期及归一到100的口径。将来运行时要保留全部十个结果、交易路径和可能重复的路径,汇报收益中位数、最好与最差、超过各自同期基准的账户数,不合并成一个资金组合,不当成十次独立验证或未来胜率。本轮只确定这些要求,不生成结果。后续如需扩大起点范围,应先另存追加实验的安排,保留本轮规则和已有结果,不覆盖旧记录,也不反复移动起点追求满意的表现。

这一轮只检查已有记录、讨论并整理规则,不运行回测,不计算账户收益,不读取或计算2023年的价格、指标、信号或收益。把已确定与待确认的内容分开写,保留原交接单。

这段可以直接交给 AI,不需要先背下每条规则。你要确认的是它准备怎么用钱,而不是替它设计程序。

如果 AI 问得太专业,就说:“先别讲程序怎么实现,拿一笔买卖举例,让我看看这两种选择有什么区别。”

先演一遍,再让账户跑

规则卡整理好了,不用马上催 AI 出收益曲线。

先让它拿几个小情景,检查有没有互相打架的约定。

和 AI 一起验收

请按刚刚整理的账户规则卡,演示“空仓时同时出现两个买入机会”“持仓期间又有新信号”“当天收盘卖出,但当天早上也有买入信号”这三个情景。它们只是规则演示,不是实测记录。用大白话说明会做什么、不会做什么,检查是否重复使用了同一笔钱、是否用了当时还不知道的信息。

再解释:如果两个错峰账户启动以后,都等到同一天才遇到第一个买入机会,规则是否允许它们买到同一笔、得到相同结果?请检查每个账户都是各自100点起步,日期偏移按市场交易日计算,指标历史没有在启动日被截断,并且各自使用匹配区间的指数参照。

如果规则卡还不能回答,请明确指出待确认项,不要临时补规则。等我确认后,再保存为账户研究交接文件,并注明回测尚未运行、2023年仍未开封。

这一次,完成的标志不是“算出来赚钱了”。

而是遇到这些情况,你和 AI 都能说明白:按这套约定,账户下一步会做什么。

蛙兄看完 AI 的情景演示,终于发现了一件事。

之前的表格没有算错。它只是在回答另一个问题。

现在,这 100 点钱有了自己的顺序:买入、等待、卖出,再继续。中间会错过多少机会,成本会扣走多少,最后会剩下多少,都还不知道。

但至少,接下来不用一边看曲线,一边临时决定该怎么算了。

“还有晚几天才来的那几位呢?”蛙兄问。

也一起算,但先别急着把十条线都铺满屏幕。

下一课,我们先跟着一个账户看清过程,再看看其他九个错开出发的账户,是不是走出了不同的结果。

课后讨论

留下你的问题、理解或不同意见,也看看其他学习者怎么想。