Stock_Analysis_For_Quant / A 股适配
A 股回测要补的六件事:复权、T+1、涨跌停、停牌、成本与字段
Stock_Analysis_For_Quant 里的示例是美股口径:Python 侧用 yfinance 取 AAPL 这类 ticker,R 侧用 quantmod::getSymbols('AAPL'),Power BI 目录里附带的是 ^GSPC.csv(标普 500)。这套口径可以直接照抄的是代码结构,不能照抄的是交易制度。
A 股多出六件事必须自己补:复权口径、T+1 交收、涨跌停与停牌造成的不可成交、显式的交易成本、交易日历,以及字段与单位差异。本站没有在本机运行过任何示例(仓库无依赖清单),所以这一页给的是改造清单与骨架,不是运行结果——官方仓库也没有任何 A 股说明。
从美股口径到 A 股研究
美股口径与 A 股口径差在哪:Stock_Analysis_For_Quant 示例里会出问题的七处
下表左列是维度,中间两列是「仓库怎么做的」与「A 股要额外做什么」,右两列告诉你出问题时去哪一层查。
| 维度 | 仓库示例里的做法 | A 股要额外处理什么 | 在仓库里哪一层会出问题 | 怎么查 |
|---|---|---|---|---|
| 数据接口 | Python 侧 yfinance、R 侧 quantmod,取的是美股 ticker | 换成 A 股可用的数据源,并确认复权参数与字段名 | 取数脚本与 notebook 的开头几格 | 打开 YFinance_Tutorial.ipynb 或 BasicStockAnalysis.R 看取数那几行 |
| 复权口径 | 示例分散在多个语言与文件里,没有统一说明用的是哪种复权价 | 先定死一种复权方式,并让指标、收益、回撤都吃同一份价格序列 | 指标计算与收益计算之间最容易口径不一致 | 看同一标的的收盘价序列在不同文件里是否同源 |
| 交收制度 | 不涉及:美股可当日买卖 | 信号日与成交日必须分离,买入当日不可卖出(T+1) | 所有策略类 notebook 与 Excel 策略工作簿 | 看信号序列是如何映射到成交价格的 |
| 涨跌停 | 不涉及:美股无涨跌停(有熔断机制,但个股层面不同) | 触及涨跌停的当日可能无法成交,需要可成交性过滤 | 策略回测的成交假设 | 看成交环节有没有对涨停/跌停做条件判断 |
| 停牌与交易日历 | 不涉及:按取到的交易日序列走 | 停牌日没有行情;交易日历不等于自然日,滚动窗口要按交易日数 | 时间序列类 notebook 与滚动指标 | 看日期索引是否连续、缺失日如何处理 |
| 交易成本 | 各示例的假设不一致,多数只给收益曲线 | 佣金、印花税、过户费、滑点都要显式加进成交价或现金流 | 组合与策略样例的收益计算段 | 看有没有扣成本的步骤,还是一条净值直接算收益 |
| 字段与单位 | 美股字段(close / volume,美元计价,股数) | A 股字段名与单位不同(手与股、元与分),成交量口径要看数据源定义 | 数据加载与列名映射段 | 核对读进来的列名与单位,尤其是成交量 |
改造六步:把 Stock_Analysis_For_Quant 的示例换成 A 股数据
每一步都给一段可运行骨架与预期输出。骨架是方法示例,不含任何收益结论,本站未在真实 A 股数据上运行。
先换数据源,再谈指标
把取数那几行换掉,其余逻辑先不动。这样出问题时能确定是数据层还是计算层。
import akshare as ak df = ak.stock_zh_a_hist(symbol="600519", period="daily", adjust="qfq") df = df.rename(columns={"日期": "date", "开盘": "open", "收盘": "close", "最高": "high", "最低": "low", "成交量": "volume"}) df["date"] = df.index预期输出:一张带
date与close的日频表,列名与你后续代码一致。若数据源换成了tushare-finance,字段名与频率参数不同,需要重做这一段映射。定死复权口径
复权方式一经确定,整条链路都必须用它:算指标、算收益、算回撤、做对比,全用同一份价格。混用是不一致的最常见来源。
# 只用一份复权价,不改口径 price = df["close"] # 已经是前复权价 ma20 = price.rolling(20).mean() # 均线也用它 ret = price.pct_change() # 收益也用它
预期输出:指标曲线与收益序列来自同源价格。检查方法:随便取一天,用
price手算一次日收益,与ret对得上,说明口径没串。加可成交性过滤(涨跌停与停牌)
A 股回测最容易高估收益的地方就在这里:信号出现,但那天根本买不进或卖不出。
# 需要数据源提供涨停价/跌停价或涨跌幅 can_buy = signal_buy & (df["close"] < df["limit_up"]) # 涨停当日跳过 can_sell = signal_sell & (df["close"] > df["limit_down"]) # 跌停当日跳过 tradable = df["volume"] > 0 # 停牌日为 0 成交量
预期输出:成交日的数量少于信号日。若两者数量完全相同,几乎可以肯定过滤没生效——先确认数据源是否给出涨跌停价。
把信号日与成交日分开
T+1 意味着买入后当日不可卖出;更稳妥的做法是统一用「次日开盘或次日收盘」成交,并给滑点留位置。
# T+1:T 日出信号,T+1 成交 df["exec_price"] = df["open"].shift(-1) # 卖出还要满足至少持有 1 个交易日 hold_days = signal_sell.index - entry_index >= 1
预期输出:成交价格序列相对信号序列错开一根。用
head()对齐看两列日期即可确认错开是否正确。显式建模交易成本
成本必须落到成交价或现金流上,而不是事后在净值上乘一个系数。四项成本的口径见下一节。
def trade_cost(amount_buy, amount_sell, rate): commission = (amount_buy + amount_sell) * rate["commission"] stamp = amount_sell * rate["stamp"] # 卖出方计 transfer = (amount_buy + amount_sell) * rate["transfer"] return commission + stamp + transfer预期输出:每笔交易有一条成本记录,净值曲线比不加成本时低,且交易频率越高差距越大。差值是否合理,取决于你填的费率。
交易日历对齐与样本基准
把交易日历与标的序列对齐,避免自然日算平均;再定死样本区间与基准,后续所有比较都在同一区间内。
# 以交易日历为基准重采样,避免自然日空转 calendar = pd.DatetimeIndex(df["date"]) aligned = series.reindex(calendar).ffill() # 样本与基准写进配置,而不是散在代码里 sample = ("2018-01-01", "2025-12-31") benchmark = "000300" # 基准代码,按你的数据源能力选择预期输出:一张与实际交易日数量一致的时间索引(无周末空行),以及一份可复制的样本配置。样本区间一旦写死,任何时候重跑都得到同一范围。
交易成本怎么建模:四项成本与它们的公式形状
本站不给具体费率数字——费率会随政策与券商合同变化。下表给的是公式形状与量级性质,具体数值请以交易所公告、券商合同与你使用的数据源为准。
| 成本项 | 公式形状 | 量级性质 | 建模位置 | 注意点 |
|---|---|---|---|---|
| 佣金 | max(成交金额 × 佣金费率, 最低收费) | 按成交金额比例收取,且通常有最低收费门槛 | 买卖两侧的现金流 | 门槛项在小额交易上影响明显;费率与门槛以你的券商合同为准 |
| 印花税 | 卖出成交金额 × 税率 | 按成交金额比例收取,历史上经历过调整,且只对卖出方计 | 卖出侧的现金流 | 只算单边;现行税率以税法与交易所公告为准,本站不给数值 |
| 过户费 | 成交金额(或成交股数)× 费率 | 按成交金额或股数比例收取 | 买卖两侧的现金流 | 沪深两市口径历史上存在差异,以中国结算公布的口径为准 |
| 滑点 | 成交价 = 信号价 ± 跳数 × 最小变动价位 | 按最小变动价位的整数倍估算,也可用成交金额的比例近似 | 成交价格本身,而不是事后扣减 | 流动性差的标的需要更大跳数;用固定比例近似时要在报告里写明假设 |
| 最小交易单位 | 买入数量 = floor(资金 ÷ 价格 ÷ 100) × 100 | A 股买入按手(100 股)取整,卖出可以不足一手 | 仓位计算与资金占用 | 取整残差会让实际仓位偏离目标权重,组合回测里要显式处理 |
| 资金占用(融资/融券时) | 占用金额 × 利率 × 占用天数 ÷ 年化基数 | 按日计息,年化基数按产品合同 | 现金流 | 不使用融资时这一项为 0;使用时要单列,不要混进佣金 |
rate 字典),改费率不用改策略代码,也便于做成本敏感性对比。复权方式选错,后面全盘皆错
三种复权方式算出来的收益率往往很接近,但价格水平相差很大;凡是涉及「价格绝对值」的环节都会被影响。
| 复权方式 | 价格序列长什么样 | 对收益率的影响 | 对均线/指标的影响 | 适用场景与注意点 |
|---|---|---|---|---|
| 不复权 | 除权除息日出现向下跳空,历史价保持当时的真实成交价 | 跳空日会被算成一次大幅下跌,长期收益被低估 | 均线被跳空带偏,容易产生假信号 | 只在需要核对当日真实成交价(如撮合、成本核算)时使用 |
| 前复权 | 以最新价格为基准向前调整,历史价随之缩水 | 收益序列连续,适合做长期收益率与波动率 | 均线与指标连续,适合做技术面回测 | 注意:每次新增除权,历史前复权价都会变,跨版本对比要对齐采集日 |
| 后复权 | 以上市初价格为基准向后调整,最新价被放大 | 同样连续,且历史价不再变化 | 指标连续,且序列稳定 | 适合做长期回测与跨期对比;但价格水平与盘口价差距大,不适合展示「现价」类指标 |
| 除权除息日附近 | 除权日前后字段值明显断层 | 断层被平滑后,收益率不再包含分红效应 | 滚动窗口跨过除权日时结果失真 | 要确认你的收益口径是「价格收益」还是「含分红的全收益」,两者不能混用 |
| 用复权价算回撤 | 回撤曲线的峰谷都来自复权价 | 不复权价会把除权跳空当成真实回撤 | 回撤区间可能被错误放大 | 回撤、波动率、Sharpe 这一类指标必须与收益同源同口径 |
| 跨标的/跨市场对比 | 两只标的的复权方式若不同,价格水平不可直接比 | 对比结果可能完全由口径差异造成 | 相对强弱指标失真 | 做对比前先把所有序列统一到同一复权方式与同一基准日 |
信号发出来了却买不到:停牌与涨跌停的六种情形
这一节处理的是「回测里能成交、现实里不能」的落差。落差越大,回测越不可信。
| 情形 | 表现 | 处理方式 | 建模要点 | 注意点 |
|---|---|---|---|---|
| 信号日涨停,次日买入 | 次日开盘一字涨停,挂单排队也未必成交 | 把「收盘价触及涨停价」的当日标为不可买入,顺延到下一个可成交日或放弃 | 需要涨跌停价字段,或按涨跌幅限制反推 | 一字板与盘中涨停的处理可以分开;口径要在报告里写明 |
| 信号日跌停,无法卖出 | 持仓被动延续,回撤被低估 | 把跌停当日标为不可卖出,顺延处理 | 卖出条件里加「未跌停」判断 | 连续跌停时顺延会导致持仓天数远超预期,要有上限 |
| 停牌期间信号失效 | 停牌日没有行情,但策略仍在算信号 | 停牌日不产生信号也不成交 | 用成交量等于 0 或缺少行情行判定 | 长期停牌会让序列断裂,滚动窗口会跨过停牌段 |
| 新股与特殊标的的涨跌幅限制不同 | 用统一的涨跌幅阈值会误判可成交性 | 按标的类型分别设阈值 | 阈值做成标的属性而不是全局常量 | 具体限制以交易所规则与标的当前状态为准 |
| 退市与长期停牌 | 样本中期标的消失 | 把标的剔除或标记,并检查是否引入幸存者偏差 | 股票池要按当期可得标的构建 | 只在最后一天用「当前成分股」筛选,就构成典型的幸存者偏差 |
| 涨跌停与 T+1 叠加 | 买入当日想跑也跑不掉 | 最短持有的判断要在可成交判断之后 | 先判可成交,再判持有天数 | 顺序写反会让「当日买入当日卖出」漏进来 |
原则一:先过滤,再统计
可成交性过滤必须发生在收益统计之前。如果先把收益算完再去掉几笔交易,剩下的收益已经包含了不可成交的那些,统计口径就废了。正确顺序是:信号 → 可成交性 → 成交 → 收益。
原则二:把假设写在结果旁边
顺延成交、放弃入场、按均价成交,这三种处理会给出不同结论。选定一种之后写进报告,并说明它对结果的方向性影响,而不是只贴一条曲线。这样读者才能判断结论的适用范围。
原则三:不做无根据的乐观假设
「涨停也能买到」「停牌也能按前收盘成交」这类假设会让回测好看,但会让结论失效。本站的建议是把假设设成偏保守一侧,再看结论是否依然成立——这正是回测审计页要解决的问题。
中文数据源路线与改造后自检清单
取数这一层可以用中文数据源技能完成,剩下的计算逻辑继续沿用你从 Stock_Analysis_For_Quant 学到的结构。
| 路线 | 覆盖内容 | 前置条件 | 与本仓库示例的关系 | 注意点与边界 |
|---|---|---|---|---|
akshare-finance | 股票、期货、期权、基金、外汇、债券、指数等行情与基本面;A 股日线支持复权参数 | 需本机安装 Python 与 akshare(1.18 及以上)及 pandas | 替换示例里的 yfinance 取数段,其余计算照旧 | 数据来自公开财经网站,可能限流;不处理实盘交易 |
tushare-finance | A 股、港股、美股、基金、期货、债券等 220 多个接口 | 需要 Tushare Pro 账号与 token | 适合需要财务与宏观数据的场景 | 接口权限与频次受账号等级约束 |
a-share-metrics-card | 单只 A 股的关键指标卡(估值、盈利质量、现金流、负债、分红、活跃度) | 无需额外 Key;不覆盖港股与美股 | 用于基本面快速核对,替代示例里的基本面段落 | 只做单只标的体检,不做组合与回测 |
| 自备 CSV / Excel | 你已导出的历史行情与财务数据 | 准备好字段规范(日期、开高低收、成交量、复权方式) | 与 Excel_Stock/ 的工作簿流程直接衔接 | 字段与单位完全靠自己保证,没有校验层 |
| 数据商 API | 机构级行情与财务,含更细的成交与停牌标记 | 账号、授权与费用 | 可替代示例的数据层,计算结构不变 | 授权范围与再分发条款要单独确认 |
| 券商/交易所官方渠道 | 成交明细、停复牌公告等 | 按渠道要求申请 | 用于校正第三方数据的涨跌停与停牌标记 | 接入成本较高,通常用于核对而非批量回测 |
| 自检项 | 通过标准 | 怎么查 |
|---|---|---|
| 复权口径统一 | 指标、收益、回撤用的是同一份价格序列 | 打印复权方式配置,并抽一天手算日收益与代码结果对比 |
| 信号与成交分离 | 成交序列相对信号序列错开且不早于次日 | 把信号日与成交日两列并排 head() 看对齐 |
| 可成交性过滤生效 | 成交笔数少于信号笔数 | 比较过滤前后的笔数;相等即视为未生效 |
| 成本已显式建模 | 每笔交易都有成本记录,净值随成本下降 | 把费率设为 0 与设为正常值各跑一次,看差异方向 |
| 交易日历对齐 | 时间索引与真实交易日数量一致 | 检查索引长度与数据源的交易日数量是否一致 |
| 样本与基准固定 | 样本区间与基准写进配置,不在代码里硬编码散落 | 改配置重跑,结果范围随之变化且可复现 |
| 字段与单位核对 | 成交量与金额的单位、股数与手数口径一致 | 用「成交量 × 均价」粗算成交额,与数据源给的实际成交额量级比较 |
| 不可成交交易有记录 | 被过滤掉的信号有清单,能说清每一笔为什么没成交 | 把过滤原因写成列并统计占比;占比过高说明策略对该标的不可执行 |
关于 A 股回测改造的常见问题
制度与费率类问题一律以交易所公告、券商合同与你使用的数据源文档为准;本页按固定提交 df4bd3c58d71 与 2026-09-30 的仓库结构整理,没有在本机运行过任何示例。
Stock_Analysis_For_Quant 能直接拿来做 A 股吗?
不能直接做。它的数据层是 yfinance(美股 ticker)与 quantmod(同样美股 ticker),Power BI 目录里附带的是标普 500 的 CSV,官方 README 与目录 README 都没有 A 股说明。可以照抄的是代码结构、指标写法与组合流程;数据层与交易制度必须自己替换和补充。以官方仓库当前状态为准。
前复权、后复权、不复权到底该用哪个?
做长期收益与指标回测,用前复权或后复权都可以,关键是整条链路只用一种。前复权的历史价会随最新除权变化,跨时间对比要注明采集日;后复权序列稳定、适合做长期回测,但价格水平与盘口价差距较大。不复权只在需要核对当日真实成交价时使用,直接拿它算长期收益会把除权跳空算成下跌。具体口径请以你使用的数据源文档为准。
T+1 在回测里具体怎么体现?
两个动作:一是把信号日与成交日分离(常见做法是 T 日发信号、T+1 成交),二是限制最短持有时间,买入当日不可卖出。写代码时注意顺序——先判断可成交性,再判断持有天数,否则会漏进「当日买当日卖」这种现实里做不到的交易。另外要注意:T+1 只约束「买入当日不可卖出」,不等于「当日买入当日不能有持仓变动」——例如盘中做加仓是允许的,受限的是卖出方向。把它写成「持有期至少一个交易日」比写成「必须隔夜」更贴近规则本身,也更容易在代码里实现。具体交收规则以现行交易制度为准。
涨跌停买不进、卖不出该怎么处理?
把涨跌停当作成交前置条件,而不是事后修正:触及涨停的当日标为不可买入,触及跌停的当日标为不可卖出,然后选择顺延到下一个可成交日,或者直接放弃这次信号。两种处理给出的曲线不同,选定一种写进报告即可。判断可成交性需要涨跌停价或涨跌幅限制字段,具体限制以交易所规则与标的当前状态为准。
交易成本要加多少?费率从哪里来?
本站不给具体数值,因为费率会随政策和券商合同变化。你需要填四项:佣金(按成交金额比例且通常有最低收费)、印花税(按成交金额比例、只对卖出方计)、过户费(按成交金额或股数比例)、滑点(按最小变动价位的跳数估算)。把这四项做成可替换的参数对象,就能做成本敏感性测试;具体数值以交易所公告、券商合同与结算机构公布口径为准。
免费的中文数据源能用吗?要注意什么?
可以用来做研究。例如 akshare-finance 覆盖股票、期货、期权、基金、外汇、债券与指数的行情与基本面,A 股日线支持复权参数,但数据来自公开财经网站,可能限流,也不处理实盘交易;tushare-finance 覆盖更细的财务数据,但需要账号与 token。替代方案是自己准备 CSV 或 Excel,这时字段与单位完全靠自己保证。数据授权与可用性以各数据源条款为准。
改造完之后,怎么判断结果能不能信?
按顺序查四件事:复权口径是否统一、可成交性过滤是否生效、成本是否显式建模、样本与基准是否固定。这四项都过之后,再去看样本外与参数敏感性——回测审计页把这份检查整理成了十二项表格与四级可信度分级。需要提醒的是,Stock_Analysis_For_Quant 的示例普遍缺少样本外与参数敏感性这两层,所以「照着示例改完之后结论依然站不住」是常见结果,不是你没改对。改造正确不等于结论可信,两者要分开判断。以本站审计清单为方法参考,具体口径以你的数据与实现为准。