风险声明:先泼冷水,再谈代码
本文是一篇技术教程,讨论的是如何用 DeepSeek 与 Python 把一个”炒股想法”翻译成可执行的量化策略代码,是量化研究的第一课,全文不构成任何投资建议。请务必记住三条铁律:第一,回测收益不等于实盘收益,一条在历史数据上年化 50% 的曲线,放到实盘可能连正收益都保不住;第二,量化策略在实盘会遭遇滑点、冲击成本、策略容量限制、流动性枯竭等回测里难以完全刻画的问题;第三,历史数据本身有偏,幸存者偏差、财报滞后披露、复权方式错误,都会让你的回测系统性”好看”。把想法量化,是把决策从情绪移向规则的唯一途径。量化是提高研究效率和纪律性的工具,不是印钞机。

一、为什么大多数炒股想法根本不可执行
做了二十年的策略研究,我见过的散户想法里,九成以上是”不可证伪”的。所谓不可证伪,就是你无法用历史数据算出一个明确的买卖信号来验证它对不对。”低买高卖”是对的废话——什么算低?什么算高?用什么价格衡量?隔多久?”追热点”也一样——热点的定义是什么?持续性多久?买入后跌多少算热点证伪?这些想法的共同问题是:缺乏可计算的规则,因而无法被历史数据检验,也就无法被改进。
量化翻译的五要素框架
一个想法要变成可执行的量化策略,必须补齐五个要素,缺一不可:
- 标的池:在什么范围内选股?全 A 股、沪深 300 成分股、某个行业?是否剔除 ST、上市不满一年、停牌的股票?
- 入场条件:满足什么可计算的信号时买入?信号必须只用决策时点之前的数据。
- 出场条件:止盈、止损、信号消失、固定持有期?必须明确优先级。
- 仓位规则:每笔买多少?等权、按波动率倒数、按信号强度?满仓还是留现金?
- 风控规则:单票最大仓位、账户最大回撤熔断、同行业集中度上限。
DeepSeek 在这一步对量化研究的价值最大:你把想法描述给它,要求它先反问补齐五要素,再动手写代码。这比直接说”帮我写个金叉策略”好十倍。
案例:把”均线金叉买入”翻译成完整规则
“金叉买入”这四个字里藏着至少六个模糊点,量化翻译就是逐个消灭它们:
- 哪两条均线?——比如 20 日均线与 60 日均线。
- 均线用什么价格计算?——收盘价还是成交均价?日线还是周线?
- “金叉”怎么定义?——短均线上穿长均,是收盘价确认上穿,还是当日盘中即可?
- 什么时候成交?——信号 T 日收盘确认,T+1 日开盘价成交(这是标准做法,避免未来函数)。
- 出了怎么卖?——死叉卖出?跌破 20 日线卖出?还是最大持有 40 个交易日强制离场?
- 仓位多少?——每次用账户 20% 资金等权买入,同持不超过 5 只。
翻译完之后的完整策略描述是这样的:在沪深 300 成分股(剔除 ST 与上市不满 120 日的股票)中,当日 20 日均线自下而上穿越 60 日均线(以日线收盘价计算),且当日收盘价高于 20 日均线,则于次一交易日以开盘价买入,仓位为账户的 20%;当 20 日均线下穿 60 日均线或收盘跌破 60 日均线时,于次一交易日开盘价卖出;单票止损 8%;持仓上限 5 只。你会发现,一段话写完,这个策略已经可以被任何回测引擎执行了——这就是量化与”盘感”的本质区别,也是量化入门的第一道门槛。
可执行 vs 不可执行的对照
| 维度 | 不可执行的说法 | 可执行的量化描述 |
|---|---|---|
| 选股 | “选强势股” | 沪深 300 中 20 日涨幅排名前 10%,剔除 ST 与次新 |
| 入场 | “趋势向好时买入” | 收盘价站上 20 日线且 20 日线斜率为正,T+1 开盘买入 |
| 出场 | “涨得差不多就走” | 死叉或跌破 60 日线卖出,或持有满 40 日离场 |
| 仓位 | “重仓龙头” | 等权 20%,单票上限 25%,最多 5 只 |
| 风控 | “感觉不对就撤” | 单票止损 8%,账户回撤 15% 强制清仓冷却 10 日 |
给 DeepSeek 的策略描述模板
以下是量化策略描述模板,可以直接复制使用,把方括号内容替换成你的参数:
请扮演资深量化研究员。在写任何代码之前,先把我下面的想法翻译成完整的策略五要素描述,
并列出所有模糊点向我确认,确认完毕后才允许写代码:
【我的想法】:[例如:均线金叉买入,死叉卖出]
请按以下结构输出:
1. 标的池:[范围 / 剔除规则]
2. 入场条件:[信号定义,需指明用什么价格、什么周期、T日信号T+1执行]
3. 出场条件:[信号消失 / 止损 / 最长持有期,按优先级排列]
4. 仓位规则:[单票权重、最大持仓数、资金分配方式]
5. 风控规则:[单票止损、账户级熔断、行业集中度]
补充约束:
- 所有条件必须只用 T 日及之前的数据计算
- 成交价必须明确(T+1 开盘价 / 当日均价)
- 如某要素我未给出,请给出 2-3 个候选方案并说明利弊,由我选择
二、数据获取:一切量化研究的地基
数据质量决定了量化研究的上限,量化研究的第一性原则是数据先行。垃圾数据进,垃圾策略出。这一节讲清楚数据从哪来、怎么洗、怎么避开最隐蔽的三类坑。
数据源选择
开源数据库
对个人量化研究来说,最常用的是这三类:
- AkShare:免费、无需注册、接口覆盖 A 股日线/财务/指数,适合快速原型。缺点是接口偶尔变动、无 SLA。
- Tushare:需积分门槛,数据规整度好,有复权因子、财报披露日期等关键字段,适合认真做量化回测的人。
- yfinance / 其他:适合美股与加密货币研究;A 股研究注意其复权处理与 A 股规则不完全一致。
券商接口(如 QMT、Ptrade)提供更实时的行情与真实可交易的路径,但门槛在开户与权限;本地 CSV 则是最后的兜底——把核心数据落盘,保证回测可复现。我的建议是:这是量化工作流的基本功:研究期用 AkShare/Tushare 拉数据后立刻存成 parquet/CSV,之后所有回测只读本地文件,避免”接口一改、回测结果全变”。
数据获取与落盘代码模板
import akshare as ak
import pandas as pd
from pathlib import Path
OUT = Path("./data"); OUT.mkdir(exist_ok=True)
def fetch_daily(symbol: str, start: str, end: str,
adjust: str = "hfq") -> pd.DataFrame:
"""拉取个股日线并落盘。adjust: '' 不复权 / 'qfq' 前复权 / 'hfq' 后复权"""
df = ak.stock_zh_a_hist(symbol=symbol, period="daily",
start_date=start, end_date=end, adjust=adjust)
df = df.rename(columns={
"日期": "date", "开盘": "open", "收盘": "close",
"最高": "high", "最低": "low", "成交量": "volume",
"换手率": "turnover"})
df["date"] = pd.to_datetime(df["date"])
df = df.set_index("date").sort_index()
df.to_parquet(OUT / f"{symbol}_{adjust or 'raw'}.parquet")
return df
# 一次拉取标的池(示例:三只股票,实盘请批量并限速)
for sym in ["600519", "000858", "601318"]:
fetch_daily(sym, "20180101", "20241231", adjust="hfq")
fetch_daily(sym, "20180101", "20241231", adjust="") # 不复权备用:算涨跌停价
复权处理:回测为什么必须用复权价
三种复权方式的区别
上市公司分红、送股、配股会造成价格跳空,这种跳空不是交易机会,而是会计事件。不复权价格会在除权日出现假跳空,均线金叉、突破信号在除权日全部失真。前复权以当前价格为基准向前调整,价格连续,适合看图,但有致命缺陷:每发生一次新的分红,全部历史价格都会重算,你上个月的回测数据和这个月对不上,且历史价格可能变成负数。后复权以上市首日为基准向后调整,价格连续、历史数据一旦生成永不变化,是量化回测的标准选择,几乎所有量化框架默认接受后复权。一句话:画图看盘用前复权,量化回测用后复权,算涨跌停价、判断实际成交用不复权。三套数据都要留着。
未来函数:量化回测的第一杀手
未来函数指在 T 日的决策中使用了 T 日之后才能知道的信息。最常见的两种:
- 用当日收盘价决定当日交易:T 日 14:55 你并不知道 T 日收盘价,若策略是”收盘价上穿 20 日线就买”,实盘只能以接近收盘的价格追买,回测却假设你以收盘价成交,收益被系统性美化。正确做法:T 日收盘出信号,T+1 开盘成交。
- 用后来公布的财报数据回测当期:年报”报告期”是 12 月 31 日,但披露日可能到次年 4 月。如果你按报告期对齐数据,一季度的回测就偷看了未来的净利润。正确做法:按披露日对齐,或给财报数据加 60-90 日的安全滞后。
技术指标里的未来函数更隐蔽:某些库的 rolling 计算默认居中窗口,zigzag、峰值检测类函数天然偷看未来。养成一个习惯——任何特征进入模型前,先问一句:这个数在 T 日收盘那一刻存在吗?
数据清洗与幸存者偏差:量化数据的隐形陷阱
清洗规则
- 停牌:停牌日无成交,信号日停牌则顺延到复牌后首个可交易日执行,不能假装成交了。
- 退市股票:回测区间内退市的股票必须保留在标的池里,退市时按最后价格强制清仓计损。
- ST 与风险警示:涨跌幅 5%、流动性差、退市风险高,通常直接剔除,但要写成规则而不是拍脑袋。
- 新股上市首日:A 股新股无涨跌幅限制(科创板/创业板前几日),波动极端,建议上市满 N 日才纳入。
幸存者偏差:被高估的收益从哪来
如果你今天打开行情软件,拉一份”当前在交易的 A 股”名单去回测过去十年,你的标的池天然剔除了所有退市股。而退市股的平均收益是深度负的。这笔”被剔除的亏损”会让你的回测年化凭空多出几个百分点——什么都没做就跑赢了指数。防御方法:使用历史时点的成分股名单(point-in-time universe),例如每个回测月末都用”当时真实存在的股票列表”,退市股包含在内。Tushare 等数据源提供指数历史成分接口,务必用它,而不是用当前成分股回测历史。
三、特征工程:给策略喂干净的原料
特征工程是量化研究中投入产出比最高的环节:同样的数据,特征处理得好坏可以让同一套回测结论天差地别。
量化因子工程的目标只有一个:量化特征工程的本质,是把原始行情变成”每个截面可比、每个时点无泄漏”的数值。
常用特征族:量化因子的基本弹药库
- 动量:过去 20/60/120 日收益率,以及去掉最近 5 日的”跳跃动量”(避开短期反转)。
- 波动率:20 日收益标准差、下行波动率、振幅均值。
- 均线偏离:收盘价 / 20 日均线 – 1,衡量超买超卖。
- 成交量与换手:5 日均量 / 20 日均量、换手率及其变化。
- 财务因子:市盈率、市净率、ROE 等,必须按披露日对齐。
标准化与去极值:不同量纲不能直接加权
换手率是百分位数量级,ROE 是个位数百分比,动量可能是 0.5 这种小数——直接把它们加权求和毫无意义,量纲大的因子会主导一切。标准流程是三步:去极值(常用 MAD 法,把超出中位数 ±5 倍绝对偏差的值截断)、标准化(减均值除标准差,变成 z 分数)、中性化(对市值、行业做横截面回归取残差,剥离风格暴露)。这三步都必须在每个截面内独立进行,绝不能在时间序列全样本上做——否则同样引入未来信息。
特征工程代码模板
import numpy as np
import pandas as pd
def winsorize_mad(s: pd.Series, n: float = 5.0) -> pd.Series:
"""MAD 去极值:截断到中位数 ± n 倍绝对中位差"""
med = s.median()
mad = (s - med).abs().median()
upper, lower = med + n * 1.4826 * mad, med - n * 1.4826 * mad
return s.clip(lower, upper)
def zscore(s: pd.Series) -> pd.Series:
return (s - s.mean()) / s.std()
def process_cross_section(df: pd.DataFrame, factor: str) -> pd.Series:
"""df 为某一交易日全市场截面数据,逐日截面处理,绝不跨期"""
x = winsorize_mad(df[factor])
return zscore(x)
def momentum(close: pd.Series, window: int, skip: int = 0) -> pd.Series:
"""动量特征:过去 window 日收益,可选跳过最近 skip 日"""
return close.shift(skip) / close.shift(window + skip) - 1
# 关键:所有特征都基于 shift 后的数据,T 日特征仅供 T+1 决策使用
# 例:df["mom20"] = momentum(df["close_hfq"], 20).shift(1)
滞后处理:T 日特征只能用于 T+1 之后的决策
前视偏差在特征层面最常见的形态是:特征算到了 T 日收盘值,回测却让它参与 T 日的交易。一行 shift(1) 是量化特征工程的最后防线。更进一步,如果你的信号依赖财报数据,滞后要按披露日计算;如果依赖公告类事件,按公告日计算。建议在代码里强制写一个断言:assert feature.index.shift(1, freq="D") <= signal_date 之类的检查逻辑,把”人会不会忘”变成”代码不许过”。
给 DeepSeek 的特征工程提示词
以下是标的池的日线数据字段:date, open, close, high, low, volume, turnover(后复权价已就绪)。
请用 pandas 为我实现以下特征,并严格遵守规则:
1. 动量:20日、60日收益率(跳过最近5日的版本也要)
2. 波动率:20日收益率标准差
3. 均线偏离:close / MA20 - 1
4. 量能:volume_5 / volume_20
约束:
- 每个特征输出前必须 shift(1),保证 T 日特征只用于 T+1 之后
- 禁止使用任何 rolling 居中窗口
- 对每个特征写一个单元测试:构造 6 根已知 K 线,人工算出期望值并断言
- 解释每个特征的业务含义,不超过三句话
四、回测:让历史数据诚实地说话
量化回测的目的不是证明策略好,而是尽量诚实地模拟”如果当时真这么做会怎样”。诚实二字,全在细节里。
回测框架怎么选:量化引擎的三条路线
| 框架 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| backtrader | 事件驱动、逐 bar 撮合、订单逻辑复杂的中低频策略 | 撮合逻辑贴近实盘,支持止损单、条件单 | 速度慢,大规模参数扫描吃力 |
| vectorbt | 向量化回测、因子研究与大规模参数扫描 | 极快,一晚扫几万个参数组合 | 撮合简化,复杂订单逻辑要自己补 |
| 自建引擎 | 特殊撮合规则、组合层面风控 | 完全可控、可复现 | 自己写的 bug 自己兜底,工作量大 |
我的建议:研究阶段用 vectorbt 快速筛想法,进入”准实盘评估”阶段用 backtrader 或自建事件驱动引擎复核一遍,两套结果对不上就说明有一套里有未来函数或撮合假设不一致。
必设的摩擦成本:量化回测的诚实底线
没有摩擦成本的回测等于没有回测。A 股的典型参数:佣金约万 2.5(含最低 5 元)、卖出印花税 0.05%(税率会变,按回测区间校准)、过户费、滑点——流动性一般的个股建议至少设 0.1%-0.2%,冲击成本随订单量增大而增加,回测里可以用”成交量不超过当日均量的 1%-5%”这类参与率限制来近似刻画容量约束。这些都是保守化参数:宁可低估收益,不可高估。
关键绩效指标:看什么、怎么看
- 年化收益:最直观也最容易被操纵(拉长区间、换复权方式都会变)。永远与基准(如同期沪深 300)对比看超额。
- 最大回撤:决定你能不能拿得住。回撤 40% 的策略年化再高,多数人也会在 -25% 时清仓。
- 夏普比率:收益/波动。A 股日线策略夏普大于 1.5(扣除成本后)就值得怀疑是否过拟合。
- 胜率与盈亏比:两者互补,胜率 90% 盈亏比 0.2 的策略是”捡钢镚被压路机碾”,一次黑天鹅亏光。
- 换手率:决定交易成本对收益的侵蚀程度。年换手 50 倍的策略,摩擦成本假设错一点,结论完全反转。
- 容量:日均成交额 5000 万的股票池,装得下 500 万的资金,装不下 5 个亿。
最小可用回测代码模板(vectorbt 风格)
import pandas as pd
import vectorbt as vbt
def run_backtest(close: pd.Series, entries: pd.Series, exits: pd.Series,
init_cash: float = 1_000_000) -> vbt.Portfolio:
"""entries/exits 为 T 日收盘产生的信号,内部统一 T+1 开盘执行由调用方保证:
这里传入的 close 应为 T+1 开盘价序列对齐后的执行价,或直接用 shift 信号。"""
pf = vbt.Portfolio.from_signals(
close=close,
entries=entries.shift(1).fillna(False), # T日信号 -> T+1执行,防未来函数
exits=exits.shift(1).fillna(False),
init_cash=init_cash,
fees=0.00025, # 佣金万2.5
slippage=0.002, # 滑点 0.2%,保守设置
direction="longonly",
)
return pf
# ---- 关键指标输出 ----
def report(pf: vbt.Portfolio) -> dict:
stats = pf.stats()
return {
"总收益": stats["Total Return [%]"],
"年化收益": stats["Annualized Return [%]"],
"最大回撤": stats["Max Drawdown [%]"],
"夏普比率": stats["Sharpe Ratio"],
"胜率": stats["Win Rate [%]"],
"交易次数": stats["Total Trades"],
}
回测的四个陷阱
陷阱一:过拟合——参数调到最优点
把均线参数从 (5,20) 试到 (200,250),总有几组参数在历史上大放异彩。但这几组参数之所以”好”,很可能只是恰好拟合了历史噪声,而不是捕捉了规律。判别方法:如果参数邻域(比如 18-22 日)的收益断崖式下跌,只有 (20,60) 一枝独秀,这就是过拟合的典型地貌;真正有效的策略在参数邻域内表现平缓。
陷阱二:样本内偏差
只在同一段历史上反复迭代策略,等于用考试原卷练习再参加同一张考卷的考试。任何在样本内的”改进”都不可信,必须留出从未参与策略开发的样本外区间做最终裁判。
陷阱三:多重检验
试了 100 个策略留下最好的那个,那个”最好”大概率只是运气最好。100 个完全随机的策略,最好的一个夏普做到 2 也不稀奇。防御手段:记录所有尝试过的策略(包括失败的),对幸存者按尝试次数做显著性折减;或使用 White’s Reality Check / SPA 检验这类专门对付数据挖掘偏差的统计工具。至少,要知道”我试过多少次”本身就是重要信息。
陷阱四:幸存者偏差
前文已述:用今天还活着的股票回测历史,收益必然被高估。补充一个更隐蔽的变体——用”今天调入成分股的名单”回测它调入前的表现,等于预知了它会被调入(调入往往伴随大涨),这同样是幸存者偏差的近亲。
回测陷阱与检验方法对照表
| 陷阱 | 症状 | 检验方法 |
|---|---|---|
| 参数过拟合 | 最优点收益突出,邻域参数收益骤降 | 参数热力图看平滑度,邻域取中位数参数而非最优参数 |
| 样本内偏差 | 样本内夏普 2.5,从未见过样本外 | 滚动窗口 / walk-forward:训练段出参数,紧邻的下一段验证 |
| 多重检验 | 第 37 个试出来的策略”特别好” | 记录全部尝试次数,做 Bonferroni 折减或 SPA 检验 |
| 幸存者偏差 | 策略年年跑赢指数但从未踩过退市雷 | 使用历史时点成分股名单,纳入退市股与 ST 股 |
样本外验证与滚动窗口:量化策略的最终裁判
标准做法是三段式:训练段(如 2015-2019)用于开发与调参,验证段(2020-2021)用于选择模型,测试段(2022-2024)只允许碰一次——碰完无论结果如何都定案,不许回头改。更严格的是 walk-forward 滚动:每年用过去五年调参、未来一年验证,把所有验证年拼接起来看整体表现,这才是策略”穿越能力”的估计。量化研究里,样本外跑赢样本内八成的策略,已经比 95% 的”完美回测”更有价值。
滚动窗口验证代码模板
import pandas as pd
def walk_forward(daily_returns: pd.Series,
train_years: int = 5, test_years: int = 1):
"""滚动窗口:每轮用 train_years 年调参、test_years 年样本外验证"""
years = sorted(daily_returns.index.year.unique())
results = []
for start in range(0, len(years) - train_years - test_years + 1, test_years):
train_end = years[start + train_years - 1]
test_end = years[start + train_years + test_years - 1]
oos = daily_returns[(daily_returns.index.year > train_end)
& (daily_returns.index.year <= test_end)]
results.append({
"训练段": f"{years[start]}-{train_end}",
"样本外段": f"{train_end + 1}-{test_end}",
"样本外夏普": oos.mean() / oos.std() * (252 ** 0.5),
})
return pd.DataFrame(results)
# 判读:所有样本外段的夏普同号且均值稳定,策略才算"活"到样本外
五、大多数人做错的地方:让 AI 先看回测,再编逻辑
这是全文最重要的一节,也是我二十年来观察到的最深的一个坑。大部分人用 AI 做量化的流程是反的:让 AI 写十个策略 → 全部跑回测 → 挑收益最高的那个 → 再让 AI 给这个策略编一套”为什么有效”的解释(什么均值回归、动量效应、行为金融学……)。这个流程的产物必然是过拟合的垃圾,因为在 100 个噪声策略里挑最好的,本身就是一次隐式的参数优化——你优化的不是参数,是策略选择。AI 再给结果补逻辑,等于让销售给已经卖出的产品写说明书:什么理由都编得出来。让 AI 先看回测结果再解释逻辑,是人类自我欺骗的最快路径,没有之一。
正确顺序只有一条,不可颠倒:先声明策略逻辑(它为什么应该有效、赚的是谁的钱、什么时候会失效),再写代码,最后才看回测结果。逻辑在前,回测是对逻辑的证伪性检验;结果在前,回测就成了从 100 次抽样里挑运气的仪式。如果你的 DeepSeek 对话记录里出现过”这个回测不错,帮我解释一下它为什么有效”这句话,请立刻删掉整个策略重新来过。
反常识观点:回测”太好看”是危险信号而不是好消息
扣除成本后年化 60%、最大回撤 8%、夏普 3 的 A 股日线策略,几乎可以断定有问题,而不是捡到宝。经验法则:一个策略的回测指标越接近”完美”,未来函数、成交假设过松、过拟合三者中至少中了一项。我见过太多人因为”回测太好了舍不得怀疑”,在实盘亏掉真金白银。看到完美曲线,第一反应应该是去找它的 bug,这是量化老手与新手最大的心态差别。
AI 的角色边界:能做什么,不能做什么
AI 能做的
- 把你声明好的规则忠实翻译成 Python 代码,并指出规则里的模糊点。
- 写数据获取、复权处理、清洗管道、point-in-time 标的池构建。
- 搭回测框架、实现摩擦成本、输出指标与图表。
- 做大规模参数扫描、walk-forward 验证、生成分析报告初稿。
AI 不能做的
- 判断策略有没有经济学逻辑——AI 会为任何结果流畅地编出解释,包括纯噪声。
- 判断某个因子未来是否还有效——因子拥挤度、市场结构变化,历史数据给不出答案。
- 承担资金风险——下单的是你的账户,AI 不为亏损负责。
一句话:在量化研究里,AI 是 infinitely patient 的研究员和程序员,不是基金经理。它没有恐惧,也没有账户。
正确的 AI 协作顺序模板(可直接复制)
与 DeepSeek 协作做量化研究,必须按以下顺序,禁止跳步:
第一步【逻辑声明】(不看任何回测数据):
"我要做一个 [策略类型] 策略。它的核心逻辑是:
它赚的是 [对手方/风险溢价] 的钱;
有效的前提假设是 [市场机制];
预计失效的场景是 [拥挤/机制变化]。
请评估这个逻辑链是否自洽,指出漏洞,此阶段禁止写代码、禁止提及历史收益。"
第二步【规则固化】:
"把上述逻辑翻译成五要素规则(标的池/入场/出场/仓位/风控),
所有条件必须可计算、无未来函数。"
第三步【代码实现】:
"实现数据管道与回测,摩擦成本按 [佣金万2.5/印花税/滑点0.2%] 设置,
输出指标并与沪深300基准对比。"
第四步【回测与证伪】(最后一步才运行回测):
"运行回测。请注意:如果结果差,如实报告,不要为了好看而调参;
如果结果好得异常(夏普>2),请优先排查未来函数与成交假设,
而不是向我报告收益。禁止基于回测结果反改策略逻辑。"
让 AI 自查未来函数的提示词
请对下面这段策略代码做"未来函数审计",逐行检查:
1. 是否存在用 T 日收盘价决定 T 日成交的路径?
2. 是否有特征没有做 shift(1) 滞后?
3. 财务/公告类数据是否按披露日(而非报告期)对齐?
4. rolling/pandas 操作是否有居中窗口或 bfill/ffill 引入未来信息?
5. 标准化、去极值是否在时间轴上跨期计算(应为逐截面)?
对每一条给出:结论(安全/有泄漏)、涉及行号、修复代码。审计通过前禁止跑回测。
六、常见问题 FAQ
问:完全不懂代码,能靠 DeepSeek 做量化吗?
能启动,但不能放手。AI 可以帮你写出全部代码,但你需要能读懂代码在做什么——至少要能看懂入场条件、成交时点、成本假设这三处。完全不懂却直接实盘,等于把钱包交给一个你无法审计的实习生。建议先小资金模拟盘跑三个月,核对回测与模拟盘的差异,再谈真金白银。
问:回测年化 30%、最大回撤 10%,实盘能预期多少?
经验上,实盘能保住回测收益的三分之一到一半就不错了。滑点、冲击成本、成交失败、市场机制变化都会持续侵蚀。更实际的心态:把回测当作”证伪工具”——回测都跑不过成本的策略直接淘汰;回测能过的策略,再经过样本外验证与模拟盘检验,才有资格谈实盘。
问:前复权和后复权到底用哪个?为什么我两个跑出来结果不一样?
回测统一用后复权。前复权的历史价格会随着每次分红除权被重算,导致同一策略不同日期跑出的回测不可复现,极端情况下历史价格还会变负。两者结果不同通常还有另一个原因:复权方式改变了均线、收益率等指标的数值,从而改变了信号。这恰恰说明复权方式是策略定义的一部分,必须固定并记录。
问:怎么快速判断一个策略是否过拟合?
三查:一查参数敏感性,最优点邻域的收益是否平滑,尖峰即过拟合;二查样本外,walk-forward 各验证段的夏普是否同号且稳定;三查逻辑,策略赚的钱有没有明确的对手方和经济学来源。三查有两查不过关,策略弃用。特别警惕”试了很多策略后挑出的最好的那个”——那不是发现,是抽签。
问:用 AI 写的回测代码,最需要人工检查哪三处?
第一,信号与成交的对齐方式:确认是 T 日信号、T+1 成交,且代码里有 shift;第二,摩擦成本是否真实设置:佣金、印花税、滑点一个不能少,没有成本的回测直接作废;第三,标的池是否为历史时点名单:用当前成分股回测十年,是幸存者偏差的重灾区。这三处错了,其余代码再优雅也没意义。
问:没有 Tushare 积分、预算有限,数据问题怎么解决?
AkShare 免费够用,配合本地落盘(parquet/CSV)可以搭建可复现的研究环境。三个补救要点:复权数据用 AkShare 的 qfq/hfq 接口自取;历史成分股可从指数公司公开资料或第三方存档获取,实在拿不到就缩小回测区间、并在报告里明确声明偏差方向;财务数据按报告期加 90 天保守滞后近似披露日。数据可以糙,但必须知道自己糙在哪。
结语:量化是一种纪律,AI 是纪律的放大器
这套 DeepSeek + Python 的量化工作流是量化研究的完整骨架,真正值钱的不是代码模板,而是顺序和纪律:先证伪性的逻辑声明,再可计算的规则固化,再无泄漏的数据与特征,再保守的成本假设,最后才允许自己看一眼回测结果——并且看的时候带着怀疑。AI 把写代码的成本降到了接近零,这意味着过拟合的成本也降到了接近零:现在任何人都可以在一晚上试一百个策略,然后信心满满地选中最幸运的那个。纪律,才是你与”用 AI 自我欺骗的大多数人”之间唯一的护城河。








暂无评论内容