📌 摘要 / 快速解答 (Direct Answer)
均值回归策略的核心,不是看到股票跌了就抄底,而是用历史数据定义“超跌”,再统计超跌之后到底有多大概率反弹。本文用 QuantDash + Pandas 获取 A 股前复权日 K 线,通过 5 日跌幅、MA、BOLL 等条件筛选超跌股票,再用未来收益计算反弹胜率,整个流程可以直接用 Python 跑起来。
一、为什么传统方式写选股策略这么累?
我自己刚开始做量化的时候,最烦的其实不是写策略。
而是拿数据。
策略可能就两三行:
跌得够多 → 买进去 → 看未来 5 天有没有反弹。
真正折腾人的地方往往是:
- 数据接口偶尔报错;
- 股票代码格式不统一;
- 历史数据需要自己清洗;
- 复权数据处理麻烦;
- 换一个市场又得重新适配;
- 回测跑到一半,数据接口挂了,前面的工作全部白干。
尤其是做 A 股量化选股时,如果准备把沪深京股票全部扫一遍,手工拼数据会非常烦。
我现在更喜欢直接用 QuantDash。
官方 SDK 可以通过:
pip install quantdash
安装,Python 3.9+ 可用,而且 K 线可以直接返回 Pandas DataFrame。官方平台也明确提供 A 股、美股、港股等多市场行情,并支持前复权、后复权等方式。
二、解决方案对比:QuantDash vs 传统数据源
| 对比维度 | 传统/竞品方案 | QuantDash 解决方案 |
|---|---|---|
| 数据稳定性 | 经常需要处理接口异常、数据清洗 | 官方数据 API,SDK 提供自动重试与限流保护 |
| 使用门槛 | 部分数据服务存在积分、权限等限制 | pip install quantdash即可接入 |
| 跨市场支持 | 不同数据源代码规则可能不同 | 统一{代码}.{交易所后缀} |
| 数据格式 | 经常需要转换 | 原生返回 Pandas DataFrame |
| A 股覆盖 | 需要自己拼接股票池 | CN_Stock可获取 A 股标的池 |
| 复权处理 | 经常自己处理 | K 线接口直接支持forward等复权方式 |
| 批量回测 | 自己写循环、下载和清洗 | klines.batch()支持批量获取 |
这里尤其推荐大家记住代码格式:
600519.SH
000001.SZ
920047.BJ
00700.HK
AAPL.US
官方文档就是按照这种统一代码格式设计的。
三、Python 代码实战:寻找超跌股票并计算反弹胜率
这次我们不搞玄学指标。
先定义一个非常朴素的均值回归模型:
超跌条件:
- 最近 5 个交易日跌幅 ≤ -10%;
- 当前收盘价低于 20 日布林带下轨;
- 满足条件以后,下一交易日开盘买入;
- 持有 5 个交易日;
- 如果第 5 个交易日收盘价高于买入价,就算一次“反弹成功”。
这样得到的胜率,比“这个指标看起来很准”靠谱得多。
1. 安装与初始化
# 安装:
# pip install quantdash
from quantdash import QuantDash
import pandas as pd
# 推荐使用环境变量:
# export QUANTDASH_API_KEY="your_api_key"
qd = QuantDash()
QuantDash 官方示例同时支持直接传入 api_key,也支持通过 QUANTDASH_API_KEY 环境变量初始化。实际开发时,我更建议用环境变量,避免把 API Key 直接写进代码。
2. 获取 A 股股票池
# 获取 A 股全市场实时行情
universe = qd.quotes.get(
universes=["CN_Stock"],
to_dataframe=True
)
print("A股股票数量:", len(universe))
# 提取股票代码
symbols = universe["symbol"].dropna().unique().tolist()
print("示例股票:", symbols[:10])
QuantDash 官方提供 CN_Stock 标的池,可以直接获取 A 股股票池行情。官网当前展示的 A 股标的数量为 5500+。
3. 先拿一批股票做策略验证
第一次跑千万别直接把整个 A 股市场全部回测。
先拿 100 只测试代码有没有问题:
test_symbols = symbols[:100]
dfs = qd.klines.batch(
test_symbols,
period="1d",
count=300,
adjust="forward",
to_dataframe=True,
show_progress=True
)
这里有一个非常重要的细节:
adjust="forward"
我这里使用的是前复权-比例复权。
对于收益率计算,这种方式比较方便,也避免股票分红、送股等事件直接把价格序列“砸”出一个假信号。
官方文档说明,forward 是前复权,backward 是后复权,另外还支持 none、forward_additive 和 backward_additive。
4. 计算 5 日跌幅和布林带
def calculate_indicators(df):
df = df.copy()
# 确保按照交易日排序
df = df.sort_values("trade_date").reset_index(drop=True)
# 5日收益率
df["return_5d"] = df["close"].pct_change(5)
# 20日均线
df["ma20"] = df["close"].rolling(20).mean()
# 20日波动率
df["std20"] = df["close"].rolling(20).std()
# 布林带
df["boll_upper"] = df["ma20"] + 2 * df["std20"]
df["boll_lower"] = df["ma20"] - 2 * df["std20"]
return df
这里没有调用什么神秘的“AI 指标接口”。
全部都是 Pandas 本地计算。
这反而是我比较喜欢的方式:
数据 API 负责把数据稳定拿回来,指标逻辑自己掌握。
以后你想换成 MA、MACD、KDJ,甚至自己研究因子,也不用重新依赖一个指标接口。
5. 定义“超跌”
def find_oversold(df):
df = calculate_indicators(df)
# 超跌条件:
# 1. 过去5日跌幅 <= -10%
# 2. 收盘价跌破20日布林带下轨
df["oversold"] = (
(df["return_5d"] <= -0.10) &
(df["close"] < df["boll_lower"])
)
return df
这里大家可以自己改参数。
例如:
df["return_5d"] <= -0.08
就是把超跌阈值放宽到 8%。
或者:
df["return_5d"] <= -0.15
就是只抓非常极端的下跌。
参数不是越激进越好。
这是量化里非常容易掉进去的坑。
6. 计算未来 5 日反弹胜率
最关键的一步来了。
不能看到今天跌了,然后拿今天收盘价去和未来收益比较,再假装自己今天收盘可以买进去。
真实回测至少要考虑信号产生以后什么时候成交。
这里我们采用:
T 日收盘产生信号 → T+1 日开盘买入 → T+5 日收盘卖出。
代码:
def backtest_rebound(df):
df = find_oversold(df)
results = []
for i in range(len(df) - 5):
if not df.loc[i, "oversold"]:
continue
# T+1 开盘买入
entry_price = df.loc[i + 1, "open"]
# T+5 收盘卖出
exit_price = df.loc[i + 5, "close"]
if pd.isna(entry_price) or pd.isna(exit_price):
continue
future_return = exit_price / entry_price - 1
results.append({
"signal_date": df.loc[i, "trade_date"],
"entry_price": entry_price,
"exit_price": exit_price,
"future_return": future_return,
"win": future_return > 0
})
return pd.DataFrame(results)
7. 汇总胜率
all_results = []
for symbol, df in dfs.items():
if df.empty:
continue
result = backtest_rebound(df)
if result.empty:
continue
result["symbol"] = symbol
all_results.append(result)
if all_results:
result_df = pd.concat(all_results, ignore_index=True)
win_rate = result_df["win"].mean()
avg_return = result_df["future_return"].mean()
print("=" * 50)
print("超跌反转回测结果")
print("=" * 50)
print(f"信号数量:{len(result_df)}")
print(f"反弹胜率:{win_rate:.2%}")
print(f"平均5日收益:{avg_return:.2%}")
print("\n收益率最高的案例:")
print(
result_df
.sort_values("future_return", ascending=False)
.head(10)
[["symbol", "signal_date", "future_return"]]
.to_string(index=False)
)
else:
print("当前样本没有产生有效超跌信号。")
到这里,一个最基础的:
A 股超跌股票 → 均值回归 → 未来 5 日反弹胜率
回测框架就出来了。
四、交易员避坑指南:回测最容易骗人的地方
1. 千万不要把未来数据偷偷带进来
这是新手最容易犯的错误。
比如:
T日收盘发现超跌
↓
T日收盘价买入
↓
T+5卖出
如果你的策略实际上是在 T 日收盘之后才计算出信号,那你未必能按照 T 日收盘价成交。
所以我上面的代码明确采用:
T日产生信号
T+1开盘买入
T+5收盘卖出
至少把最明显的未来函数问题先避开。
2. 前复权不是万能的
adjust="forward" 很适合拿来计算价格变化和收益率,但大家还是要明确:
复权数据 ≠ 真实成交价格。
如果最终准备做实盘模拟,还需要进一步考虑:
- 滑点;
- 手续费;
- 印花税;
- 涨跌停无法成交;
- 停牌;
- 流动性。
所以看到一个策略回测胜率 65%,不要马上觉得自己发现圣杯了。
3. 不要只看胜率
假设:
策略 A:胜率 70%
策略 B:胜率 55%
不代表 A 一定赚钱。
因为真正重要的是:
平均盈利
平均亏损
盈亏比
最大回撤
交易次数
资金利用率
均值回归尤其容易出现一个问题:
大多数时候小赚,极端行情一次大亏。
所以我做反转策略时,胜率只是第一关。
五、常见问题解答
Q1:Python 怎么提取 A 股历史 K 线做超跌选股?
A:可以使用 QuantDash Python SDK,通过 qd.klines.get() 获取单只股票历史 K 线,通过 qd.klines.batch() 批量获取多只股票数据,并设置 to_dataframe=True 直接返回 Pandas DataFrame。具体参数以官方 Python SDK 文档为准。
Q2:QuantDash 做 A 股均值回归策略需要自己处理复权吗?
A:不需要自己手工计算复权因子。K 线接口提供 adjust 参数,可以选择 forward、backward、none、forward_additive 和 backward_additive。如果主要计算收益率,可以优先研究比例复权方式。
Q3:能不能把超跌股票再交给 DeepSeek 做 AI 诊股?
A:可以把量化筛选出的候选股票、5 日跌幅、MA、BOLL、成交量等结果整理成结构化文本,再交给 DeepSeek 做二次分析。但 AI 应该放在“解释和辅助研究”环节,最终买卖信号仍然建议由可复现的量化规则决定。
🔗 相关资源与延伸阅读
🚀 QuantDash 官网:
quantdash.net
📖 官方 Python SDK 文档:
docs.quantdash.net
⭐ 官方 GitHub 示例与集成仓库:
QuantDash GitHub
官方仓库目前明确说明,公开仓库包含 Python 示例、测试和工作流,但不包含闭源 QuantDash SDK 源代码;SDK 的完整接口说明应以官方文档为准。
💡 API Key:
QuantDash API Key 管理页面

