← 返回教程库

行情数据里那些会让你回测结果全错的坑

最后更新 2026-08-19
📚 量化与 AI 投研 📖 E 线 · 起步 ⏱ 约 24 分钟 R2 · 注意风险
你将学到
  • 说得出复权、停牌、退市、分红送转、涨跌停这五个坑各自污染的是数据的哪一部分,以及为什么它们全都不会抛异常
  • 看到一条异常的收益曲线或一条可疑的成交记录时,能反查到是这五个坑里的哪一个
  • 对每个坑各做一个当场就能完成的确认动作,判断自己这份数据有没有中招

你花了一个周末把数据管道接好了。接口调通,几千个代码全量下载,落到本地,读出来是一张整整齐齐的表:日期、开、高、低、收、量。没有报错,没有缺列,行数对得上交易日历。

然后你跑了第一个策略,看到一条向上的曲线,心里那一下轻微的兴奋,你大概能想象。

这一篇要说的事情不太友好:上面这段描述里的每一步,都可以完全成立,而那份数据依然是错的。 不是「有噪音」的那种错,是「结论整个反过来」的那种错。

而且——这是最要命的地方——这五个坑没有一个会抛异常。 你写代码时习惯的那套反馈机制在这里全部失灵:程序不崩,日志不红,表格不缺格,回测照常跑完,图照常画出来。数据坑不像 bug,bug 会喊,数据坑只会安静地把结论改掉。

所以这一篇不讲「怎么把数据下下来」,那是 动手之前:环境和数据从哪来 的活;也不给系统化的质检清单,那是 拿到一份数据,先跑哪几项自查 的活。这一篇只干一件事:把五个坑一个个摊开,告诉你中招之后你会在结果里看到什么样的异常,以及一个当场就能做完的确认动作。

先约定一个说法:下面反复出现的回测,指的是拿历史行情把一套交易规则重放一遍,看看按这套规则操作会赚多少。它是个模拟,不是真的下单。

坑一:复权——同一段历史,三种价格,只有一种是你要的

先说人话

你在给一家小店记流水,记的是「每天卖出多少钱的货」。老板某天做了件事:把原来一份装的商品拆成两份卖,单价对半砍。

第二天你翻账本,看到单价从二十块变成十块,第一反应是「跌了一半」。可实际上什么都没跌——同样的东西,只是换了个包装规格。你的账本没记错,你的减法也没算错,错在你拿拆分前后的两个数直接相减,而它们根本不在同一个计量单位上。

股票会做同样的事,而且花样更多:送股、转增、拆股、现金分红、配股。每一次动作之后,屏幕上的价格都会机械地往下掉一截,掉的那一截不是跌,是股本或者现金被换了个位置。

投资层:三种口径,各自在回答什么问题

处理这件事的做法叫复权,就是把历史价格按同一个尺子重新缩放,好让不同时间的价格能直接比较。常见的有三种口径:

不复权,也就是当时屏幕上真实显示的那个价。它的用处是回答「那天挂单要挂多少钱」这类和真实价位绑定的问题。

前复权,以最近的价格为基准,把更早的历史价格按比例缩下来。好处是最新那一段和你今天在软件里看到的数字对得上。

后复权,以最早的价格为基准,把后面的价格按比例放大。好处是历史不会随时间变动。

看起来只是三选一,但选错的后果不是「差一点」。用不复权的数据算收益率,你会在每一个除权日读到一次不存在的暴跌;反过来,用前复权的数据判断「股价低于多少钱」这类绝对价位规则,你比的根本不是当时的真实价格。

还有一条最阴的:前复权的历史,会随着「今天是哪天」而变。 因为它以最近为基准,每发生一次新的除权事件,整段历史都要重新缩放一遍。这意味着你上周跑的那次回测和这周跑的同一次回测,喂进去的价格序列已经不是同一份了——结果对不上,你会先怀疑自己代码改坏了,查半天什么也查不到。

顺带说一句,判断一只票离「按价格标准退市」还有多远,看的是不复权的价。如果你的数据管道里所有价格都被统一做成了前复权,这类判断从一开始就问错了对象。这一层和 幸存者偏差 那篇讲的是同一件事的两个侧面。

机制层:数据里到底存了什么

看两个开源项目怎么落地这件事,比看定义有用得多。

qlib 的行情整理脚本里有一段处理复权价的逻辑,做法是:先用「复权后收盘价除以原始收盘价」得到一个因子,再把开高低收四列都乘上这个因子,把成交量除以这个因子。乘和除方向相反,是为了让「价格乘股数」这个金额保持不变——股数变多了,单价就得变小。

接着还有一步,很多人第一次看到会愣一下:脚本会把所有字段再统一除以「这只票第一个有效交易日的收盘价」。做完这一步,qlib 库里存的那个收盘价已经不是钱了,是一个相对第一天的比值。 所以你读出来看到零点几这样的数,不是数据坏了,是它压根就没打算存真实价格。

那真实价格哪去了?存在另一个叫因子的字段里。qlib 撮合模块的注释写得很清楚:这个因子是拿来做「整手取整」用的——要把仓位换算成实际能下单的股数,就得先把价格还原回真实量级。同一段注释还说,如果收盘价存在而因子缺失,整手取整会被直接关掉,并打一条警告说明订单将按调整后的价格走。一个缺失的辅助字段,静悄悄地改掉了下单粒度,而这件事只体现在一行日志里。

另一个项目 Vibe-Trading 里有一个专门做 A 股复权的模块,模块开头那段说明值得逐句读。它先点明所用的那个数据接口返回的是未复权价,因此跨过除权日去算「收盘价对收盘价」的涨跌幅,量到的是机械跳空而不是真实波动。然后它给了自己核对过的样本:某只成分股在某个除权日,未复权算出来是负四十个百分点出头,而真实涨跌幅是正五个多百分点;另外两个样本分别是负三十四对负一,负二十四对正六。

这三个样本里最有价值的不是数值,是它接下来那句判断:这个误差永远是负的,因为价格在除权日只会往下跳,所以它是收益序列的系统性污染,不是噪声。 这句话决定了它的严重程度——噪声会互相抵消,系统性偏差不会,它只会朝一个方向越积越多。

同一个模块还有两个实现细节值得记住:它按窗口里最后一根的因子做基准来缩放(也就是前复权的做法),成交量按同一个比例反向除,而成交金额那一列保持不动——因为金额是现金,它不随股数的计量单位改变。以及,当因子缺失或者不可用时,它的选择是直接把这只票丢掉,而不是硬着头皮用被污染的价格。这个取舍方向值得学:宁可少一只票,不要多一段假数据。

你会看到什么异常

  • 净值曲线上出现一些莫名其妙的单日断崖,事后查那天并没有什么大事;
  • 把每日收益率的分布画出来,左边尾巴上孤零零挂着一小撮极端负值,而且它们在日历上扎堆(因为分红送转有季节性);
  • 同一套策略、同一段数据,只是把回测结束日期往后挪了几天,开头那几年的结果就变了;
  • 用绝对价位写的规则表现得特别好或者特别差,改成用比例写就恢复正常。

当场确认一下

挑一只你知道分过红或者送过股的票,在你的数据里找到那个除权日,用「当日收盘价除以前一日收盘价减一」算出涨跌幅,再和行情软件上那天显示的涨跌幅对一下。对不上,而且你这边明显更难看,就是没复权。

再做第二个动作:同一只票、同一个起始日期,分别用两个不同的结束日期各导一次数据,比对最开始那几天的价格。变了,说明你用的是前复权,那么你所有「历史结果」都带着一个隐含前提:今天是哪天。 手上没有现成工具的话,站内那个 前复权后复权换算器 可以把三种口径并排摆出来,先建立起数量级上的体感。

坑二:停牌——那几天没有价格,而你的表里有

先说人话

你每天记录小区门口那家面馆的一碗面多少钱,记了三年。中间有两个月,面馆装修停业。

这两个月你的表格怎么填?留空,还是照抄停业前那个价?

两种填法都不算撒谎,但它们会让下游的人得出完全不同的结论。照抄的话,看表的人会看到「连续两个月价格一动不动」,如果他正在找「价格最稳定的时段」,恭喜,他找到了一段店根本没开门的日子。留空的话,另一个按「最近六十条记录」算平均价的人,会不知不觉把时间跨度拉长到四个月。

投资层:填法本身就是一个假设

一只票停牌,意思是这段时间它根本不交易——没有成交,没有报价,你既买不进也卖不出。而你的数据表是按交易日历排的,那几行总得填点什么。

照抄前一天的价,会让这段时间的波动变成零。 任何和波动、和「横盘整理」有关的指标都会被点亮:均线走平、波动率归零、通道收窄。如果你的规则是「盘整之后突破就买」,停牌期就是一台专门给你制造假信号的机器。而复牌那天往往是一个大幅跳空,于是你的回测记录里会出现一笔漂亮的「买在最后一天、第二天大涨」——现实中你根本没机会在那几天下单。

留空也有它的坑,而且更隐蔽。 如果你的指标是按「多少行」而不是按「多少个自然日」算的,那么跨过停牌期的那条均线,覆盖的日历跨度比你以为的长得多。更糟的是有人会顺手 dropna 一下,行没了,日期就断了,而后面的代码还在假设相邻两行是相邻两个交易日。

机制层:两条不同的路子

qlib 选的是「留空并且明确标记」。它的撮合模块注释里写着一条约定:如果收盘价是空的,那这只票当天视为停牌。 数据整理阶段也配套地做了动作——成交量为零或者缺失的那些行,整行的字段都被置为空值,另外还专门加了一个停牌标记字段,以及一个记录「连续停牌了多少天」的字段。

这个约定的下游影响很直接:撮合模块在计算「今天这只票能不能买、能不能卖」的时候,会把停牌标记直接并进涨跌停限制里——停牌的日子既禁止买入也禁止卖出,和封死涨跌停一样对待。一个数据层的空值,通过一条约定,变成了交易层的一道硬门。 这就是一条数据管道该有的样子:约定写在注释里,效果落在代码里。

freqtrade 面对的是另一个市场,那里没有停牌,但网络中断、接口抽风会让某几根 K 线整根缺失。它的补齐函数做法是:用前一根的收盘价去填开高低收四个价,成交量填零。 用前值填价格,是为了让时间序列保持连续;而把成交量填成零,是留一个明确的记号,让下游能识别出「这一根是补出来的,不是真的」。

这里还有个细节值得所有人警惕:这个函数会统计补了多少行,但只有当缺失比例超过百分之一时才按 info 级别写日志,否则只写 debug。换句话说,默认的日志级别下,少量补齐是静默发生的。 你不会看到任何提示。

两条路子的取舍不一样,但共同点是:它们都把「这里的数据是假的」这件事,用某种可检测的方式留在了数据里。 你自己搭管道时,这是最该抄的一点——填可以填,但必须留记号。

你会看到什么异常

  • 成交明细里有一笔买入或卖出,日期落在那只票根本没交易的那段时间里;
  • 某笔交易贡献了一天几十个点的收益,翻回去看,前面是一段完全水平的价格;
  • 波动率类指标在某几段时间读数为零或者极小,而这几段在不同票上并不重合;
  • 成交量列出现连续的零或者空值,你的引擎却在这些行上成交了。

当场确认一下

把你数据里「成交量为零或者缺失」的行全部筛出来,然后做两件事:第一,看这些行有没有被你的信号选中;第二,看你的回测引擎在这些行上有没有真的记成交。

第二条要是中了,问题就不在数据了,在你的引擎缺一道可交易性检查——那件事属于成交假设的范畴,回测里的成交假设 那篇把这类检查按宽松程度排成了一把尺子,你可以对着看自己站在哪一格。

坑三:退市——你的股票池是一份赢家名单

先说人话

你想知道一个小区这几年住得舒不舒服,于是在业主群里发问,收到二十条回复,大部分说挺好。

问题是,住得受不了的那些人,房子已经卖了,群早就退了。群成员名单本身,就是一次筛选的结果,而这次筛选恰好和你要问的问题高度相关。

投资层:这个坑在工程上的入口只有一行代码

你的股票池是怎么来的?绝大多数人写下的是类似「取一下当前的成分股列表」这么一行。这一行的语义是「今天还在这个名单上的那些代码」,而你接下来要拿它去跑过去十年。

于是你的模拟世界里的那个人,在十年前就已经知道了谁能活到今天。退市的、被吸收合并的、连年亏损被换掉主业的,全都不在他的候选里——而这些恰恰是历史上表现最差的那一批。 这个偏差为什么致命、它在 A 股有哪些具体形态,幸存者偏差 那篇讲得很细,这里不重复。

我要补的是工程侧:股票池有三种拿法,代码上只差一个函数名,含金量差一个量级。

第一种,今天的成分列表。最省事,也最脏。第二种,带生效日期和失效日期的历史成分表——每一行是「某代码在某段时间里属于这个池子」,你按日期去查。第三种,全市场代码,含已经退市的,自己按时点过滤。

机制层:好的数据格式会逼你做对

qlib 的股票池文件用的就是第二种格式:每一行是代码加上一对起止日期,取池子的时候必须带上时间参数,由框架按时间过滤。这个格式本身就是一种约束——它让「不带时间地取一份名单」这个动作变得不自然。 你自己设计数据结构时可以借这个思路:把容易犯的错设计成写起来别扭的那条路。

qlib 那份众包数据的说明文件还坦白了一件事:公开数据源可能缺失已退市股票的数据,而这会把幸存者偏差引进来;它给出的应对是把多个来源合并、互相交叉校验,好凑出一份更完整的历史。注意这个应对方式的言外之意——单一免费数据源基本上不可能给你一份干净的历史名单。 这不是你写代码能解决的问题,是你选数据源时就已经决定了的事。

你会看到什么异常

  • 整段历史里,你的股票池成员数量是一条水平线。真实的成分名单每次调整都会有进有出,恒定的数量说明你用的是今天的快照;
  • 很早的年份里出现了当时还没上市的代码;
  • 回测的所有持仓中,从来没有一只是「一路跌下去然后消失」的,最差的也只是跌了不少然后还在;
  • 你的策略在「垃圾票」这一端的表现异常地好——因为真正变成垃圾的那些,样本里没有。

当场确认一下

按日期统计一下你股票池每天的成员数,画成一条线。这条线该是台阶状的,不该是一条直线。 然后抽查最早那一年的名单,挑几个代码去查它们的上市时间——如果有上市时间晚于那一年的,说明你的池子是从今天倒推出来的。

坑四:分红送转——复权做对了,钱还是可能对不上

先说人话

院子里一棵果树,年年结果。你每年称一次树的重量,用来判断它长得好不好。

有一年你摘了两百斤果子,年底一称,树比去年轻了。你能说这棵树在退化吗?显然不能——那两百斤在你筐里。

于是你改进了记账方式:把摘走的果子折回树重里再比较。这下曲线漂亮了。但这里悄悄多了一个前提:你假设那两百斤果子还长在树上。 而事实上它们已经被吃掉、送人、或者卖了换成了别的东西。

投资层:复权价隐含了一个你未必做得到的动作

坑一说的是「口径用错」,这一坑说的是「口径用对了之后,剩下那个差」。

现金分红这件事,价格序列本身表达不了。 公司把一部分现金发给你,股价机械地扣掉相应的一截。不复权的价格序列里,这部分收益直接消失了;而前复权和后复权把它折回价格里,代价是引入一个假设:你在分红当天,按当天的价格,把这笔现金原样买回了同一只股票。

现实里这个假设有几处漏水。分红到账有时间差,这几天钱在路上,没有参与涨跌;到手的金额通常凑不满一个交易单位,零头买不进去;不同持有期限下红利部分的税负处理也不一样,具体口径以现行税收规定和你券商的实际扣收为准。

单看一次,这些都是小钱。但复权数据是把每一次都按「立刻、无成本、原价买回」处理的,几年几十次累积下来,复权价算出来的长期收益会系统性地高于你实际能拿到的。这个差在低换手、长持有的策略上最明显——恰恰是那类策略最爱拿长期回测说事。

配股比分红更麻烦。 配股需要你自己掏钱认购,不掏就被稀释。复权因子处理配股时,默认的是「你参与了」。而实盘里你可能压根没看到那条公告,或者当时账上没钱。一个默认参数,替你做了一次真金白银的投资决定。

机制层:现金那一列不参与缩放

前面提过的那个 A 股复权模块有个细节,放在这一坑里看才显出意义:它缩放开高低收四个价,反向缩放成交量,而成交金额那一列原样不动。理由它写在注释里——金额是现金数字,它不随股数的计量单位改变。

这条处理透露了复权的本质:复权只是给「价格」和「股数」换了一个统一的计量单位,它并不改变任何一笔真实现金流。 你要是把复权理解成「把分红加回收益里」,就会在算实际到手金额的时候错得离谱。

qlib 那边的注释也标了同一件事的另一面:它引用的数据源里,有一个字段是只按拆股调整过的收盘价,另一个字段是同时按分红和拆股调整过的收盘价。两个字段之间的差,就是分红那部分。 知道这一点,你就能自己算出「这段收益里有多少来自分红」——这是个很有用的诊断量,可惜多数管道在合并数据时就把其中一个字段丢掉了。

还有一处口径陷阱:拿你的策略和大盘比的时候,对面那个指数是不是也把分红算进去了?价格口径的指数和全收益口径的指数是两条不同的线,长期下来差得不小。用复权后的策略收益去比价格口径的指数,你会白白多出一截「超额」。这一层在 基准与超额 那篇有系统的说法。

你会看到什么异常

  • 长期回测的年化收益比你按同样规则手工估算的高出一截,怎么查都查不出哪笔交易有问题;
  • 策略跑赢基准的部分,随着回测年限拉长而单调变大,且没有明显的来源;
  • 用复权价算出的「累计涨幅」和你按实际成交价加上实际到账分红算出来的对不上;
  • 某只票在某个日期的持股数量凭空变了,账面金额却连续——那是送转被折进了因子,而你的持仓记录用的是原始股数。

当场确认一下

挑一只常年分红的票,用你的数据算两个累计涨幅:一个用复权价,一个用不复权价。两者之差,就是被折进价格里的那部分。

然后对着这个差问自己一句:这笔钱在实盘里,我真的能在同一天、按同样的价格、一分不少地投回去吗? 想清楚这句,你就知道自己的长期回测该往下打多少折。先把一次除权的账算清楚,再去看几十次累积起来是多大一块。

坑五:涨跌停——回测里成交了,实盘里根本没人卖给你

先说人话

早市限价,某样菜今天最高只能卖到某个价。可这个价明显低于摊主的成本,于是摊主干脆不出摊了。

现在有人拿着当天的价目表做统计:这样菜今天的价格是那个限价。表上确实印着这个数,可市场上一斤都没有成交。价目表上有价,不等于你买得到。

投资层:这一坑的工程问题不在数据,在你的检查

涨跌停的机制层面已经被 回测里的成交假设 讲透了,这里只说动手时的那一部分:你的回测引擎里,那道「今天能不能成交」的检查,用的是哪根 K 线的哪个价?

这个问题听起来很技术,但它的答案决定了你的回测是不是在偷看未来。

机制层:一道防作弊的检查,本身可以是作弊的

Vibe-Trading 的 A 股引擎里有一个判断涨跌停是否挡住成交的函数,它的注释记录了一次修复,我认为这是整篇里最值得抄进笔记的一段。

修之前的做法:用当前这根 K 线的收盘价推算当日涨跌幅,据此判断是不是封板。听起来天经地义——今天涨了多少,看今天收盘不就知道了?

问题在于:注释直接点破,这是前视,而且两个方向都错。一只开盘就封死、尾盘打开的票,按收盘算不算封板,于是被允许在那个封死的开盘价上成交——这笔交易现实中不存在;反过来,一只开盘正常、尾盘才涨停的票,按收盘算是封板,于是被拒绝了一笔本来能成的交易。

修之后的做法:合法价带由下单之前市场就已知道的基准价算出来(前收盘价,取不到就用前一根的收盘价),再拿引擎实际会成交的那个价(本根 K 线的开盘价加上滑点)去跟这条带比。注意这里的对齐关系——比的不是「今天涨了多少」,而是「我要成交的那个价,在不在今天允许的范围内」。

这段修复的通用价值在于:一道用来防止不合理成交的检查,如果它自己用了当时还拿不到的信息,那它就从防线变成了漏洞。 而这类错误在测试里几乎不会暴露,因为它不影响任何一条断言,只影响收益数字。同一套引擎里另一个函数还写明了这条规则的适用面:这道价带检查是所有带日内涨跌幅限制的市场共用的。

同一个文件里还有一个函数按代码前缀猜这只票属于哪个板块、对应多大的幅度限制,而它的注释自己承认了一句:带风险警示标记的票单看代码前缀判断不出来,所以那一档是个启发式的近似。这句坦白比代码本身更有教学价值——幅度限制是会变的监管参数,把它硬编码进代码,等于给自己埋了一颗定时炸弹,具体幅度请以现行交易规则和你券商的实际口径为准,本文一个数都不给。

qlib 那边的做法是把这件事做成可配置的:涨跌停限制既可以传一个阈值,由框架拿当日涨跌幅去比,也可以传两个表达式,由你自己定义什么叫封板;最后再把前面说过的停牌标记并进去。默认阈值是多少、默认表达式怎么写,以你手上那个版本为准——这正是那类「你以为是常识、其实是默认值」的东西。

你会看到什么异常

  • 成交明细里出现「买入当天就赚了接近一个板」的记录,而且不止一笔;
  • 信号触发日和实际成交日百分之百重合,一次落空都没有——真实市场里不可能;
  • 策略在重大消息密集的日子里表现特别好;
  • 把回测里的成交量和同期该票的真实成交量比一比,你的量占比高得离谱——那说明你不只买不到那个价,连那个量都没有。

当场确认一下

把成交明细里「成交当日涨跌幅接近限制边缘」的记录挑出来,数一数它们占总笔数的比例,再算一算这些笔贡献了总收益的多少。如果拿掉它们收益就塌了,那你的策略是建立在一批买不到的价位上的。

然后打开你引擎里那道涨跌停检查,只看一件事:它算涨跌幅用的基准,是不是下单那一刻之前就已经确定的数。 用当日收盘价算的,立刻改。

五个坑放在一起看

它污染的是什么 你会先看到什么 一句话自查
复权 价格序列的计量单位 曲线上莫名的单日断崖,或换个结束日期结果就变 拿一个除权日的涨跌幅跟行情软件对一下
停牌 那几行本来不该有的数据 成交记录落在没交易的日子里,波动率归零 筛出零成交量的行,看有没有被信号选中、有没有真成交
退市 股票池的成员名单 池子成员数是一条直线,持仓里从来没有跌没的票 按日期统计池子规模,看是不是台阶状
分红送转 收益里那部分不体现在价格上的钱 长期收益比手工估算高一截,超额随年限单调变大 复权与不复权的累计涨幅之差,实盘真拿得到吗
涨跌停 成交的可行性,而不是数据本身 买入当天就接近一个板,信号从不落空 那道检查用的基准价,下单前就确定了吗

摆在一起就能看出它们的共性:没有一个会报错,而且四个半都朝着让结果变好看的方向错。

说「四个半」是因为复权那个坑有点特殊,值得单独说一句。不复权的数据会在除权日读出一次机械暴跌,单看它是朝难看的方向错的。可如果你写的是一个「跌得多就买」的策略,这些不存在的暴跌就变成了一批免费的黄金坑——同一个数据错误,换一个策略方向,就从保守变成了作弊。 这也是为什么「这个偏差是往好还是往坏错」这个问题没有通用答案,得结合你的规则一起看。

还有一件事我想单独提醒:这五个坑不是并列的,它们卡在数据流水线的不同工位上,前面的坑会改变后面那些坑的表现形式。比如复权口径错了,涨跌停的判断跟着就错,因为算涨跌幅的基准价被换掉了。整条流水线上一共有哪几道工序、每道工序对应哪种偏差,回测偏差地图 那篇画成了一张图,建议对着看一遍。

至于把这些确认动作固化成一份每次拿到新数据都跑一遍的例行检查——那是 拿到一份数据,先跑哪几项自查 的事情。这一篇给你的是「知道该找什么」,那一篇给你的是「每次都别忘了找」。两件事分开做,别指望一份清单能替你理解机制。

最后补一句和写代码直接相关的:把数据修干净之后,规则本身还有一整套歧义要消,「均线金叉就买入」这句话,程序其实一个字也看不懂 讲的是那一段。数据的坑和规则的坑是两回事,两边都干净了,你的回测才算勉强站得住。

⚠️ 风险提示

本文讲的是历史行情数据在工程处理上的常见缺陷与自查方法,不构成任何投资建议、买卖信号或收益承诺,不推荐任何数据源、框架、平台或标的。文中所有关于开源项目的描述,均来自对指定快照版本源码与文档的阅读,不是运行结果,也不保证在你的版本、你的数据源、你的市场上同样成立——具体行为请以你手上那个版本和你自己的核对为准。文中提到的涨跌幅限制、退市标准、税负处理等,全部由现行监管与交易规则决定并且会变化,本文不给任何数值。把这五个坑全部修好,也只是让回测不再明显失真,它依然不等于实盘结果,历史表现更不预示未来收益。 完整风险提示见 免责声明

小结

  • 这五个坑的共同点是不报错:程序跑完,图画出来,日志干净,而结论已经变了。你平时依赖的那套「有问题会喊」的反馈机制,在数据层面完全失灵。
  • 复权决定价格序列的计量单位。三种口径各有各的用途,用错的代价不是差一点;尤其记住前复权的历史会随着「今天是哪天」而变,这是很多「结果复现不了」的真正原因。
  • 停牌那几天本来没有数据,你填了什么,就等于替市场做了什么假设。照抄前值会制造零波动和假突破,留空会让按行数算的指标跨度失真。好的管道要么留标记(成交量置零),要么留空值并明确约定其含义。
  • 退市这个坑的入口只有一行代码:你取的是今天的名单,还是带生效日期的历史名单。池子成员数是一条直线,就是最明显的信号。
  • 分红送转里藏着复权做对之后仍然存在的那个差:复权价隐含了「分红当天原价买回」这个你未必做得到的动作,而配股更是替你做了一次掏钱的决定。复权只换计量单位,不改变任何真实现金流。
  • 涨跌停的问题往往不在数据,在你那道检查本身。判断能不能成交,基准价必须是下单前就已知的;用当日收盘价去算,那道防线自己就在偷看未来。
  • 每个坑都配了一个当场能做完的确认动作,不需要搭任何框架,一段几行的比对就能给你答案。先做确认,再谈优化。

一句话说完这篇:你手上那份行情表看着整整齐齐,可里面的价格可能换过尺子、停业的日子被人替你填了数、活不下去的早就被删掉了、发到手的钱没算进去、还有些买卖你在现实里根本做不成——这五件事一个都不会报错,所以只能你自己一件件去问。

内容有错、看不懂、或想看下一篇?告诉我们 →

本文为投资教育整理,不构成任何投资建议;不荐个股/基金、不预测点位、不承诺收益。关键数据与结论请结合权威来源自行验证,并见风险免责声明