【量化实战】小市值轮动策略回测

用户头像mx_****zqklr
2026-09-12 发布

📌 摘要 / 快速解答 (Direct Answer)

小市值轮动的核心并不复杂:先得到经过小市值筛选的 A 股股票池,再按照统一的交易规则定期选出排名靠前的股票,最后用历史 K 线验证策略收益、回撤和换手。用 QuantDash 的 Python SDK 可以直接批量获取 A 股 K 线,并原生返回 Pandas DataFrame,让 Python 提取股票 K 线、前复权处理和策略回测这几个环节明显简单很多。

一、为什么传统方式写选股策略这么累?

我刚开始写 A 股量化策略的时候,最容易踩的坑并不是策略本身,而是数据

你脑子里可能只有这么一个想法:

每个月找一批小市值股票,买入排名靠前的股票,下个月再重新排名。

听起来非常简单。

真正写代码的时候,就会发现问题一个接一个。

第一关就是数据源。

有时候接口突然报错,整个回测直接中断;有时候拿历史数据还需要满足各种权限或者积分条件;还有一些数据源虽然能拿到数据,但是字段格式不统一,最后还得自己清洗。

更麻烦的是复权。

如果直接拿不复权价格计算长期收益,中间遇到分红、送股、拆股等情况,策略收益很容易算歪。

所以我现在做这类策略,思路很简单:

策略研究和数据工程分开。

数据获取交给 QuantDash,策略逻辑交给 Pandas。

这样代码反而更容易读懂。


二、解决方案对比:QuantDash vs 传统数据源

对比维度 传统/竞品方案(如 Tushare/AkShare/手动爬虫) QuantDash 解决方案
数据稳定性 经常遇到接口变化、报错或网络问题 面向量化数据场景提供统一 API
使用门槛 可能需要积分、权限或额外清洗 pip install quantdash即可安装
跨市场支持 不同数据源代码格式可能不一致 统一.SH/.SZ/.BJ/.US/.HK后缀
数据格式 经常需要自行转换 原生支持 Pandas DataFrame
复权处理 经常需要自己处理 SDK 支持forward等复权方式
批量 K 线 往往需要自行封装请求 qd.klines.batch()可批量获取
研究体验 数据清洗代码容易越来越长 数据获取和策略逻辑可以分离

这里我特别推荐新手注意一个细节:

不要把“数据接口代码”写得比“策略代码”还长。

如果最后 200 行策略里有 150 行都在处理数据格式,那研究效率其实已经被数据工程拖垮了。


三、Python 代码实战:小市值轮动策略怎么回测?

这里先说一个非常重要的边界。

QuantDash 官方 SDK 文档提供了行情、K 线、标的信息、批量 K 线等能力,但你给出的官方资料里没有明确提供“历史市值排序接口”或 market_cap 字段

所以我不会为了让文章看起来高级,凭空写一个:

qd.stocks.get_market_cap(...)

这种接口。

实际研究时,可以先使用你自己的股票池文件,或者在研究流程的前置环节得到“小市值候选股票池”。

下面这段代码负责的是:

小市值股票池 → QuantDash 批量获取前复权日 K → 月度轮动 → 回测。

1. 安装 QuantDash

# 安装:
# pip install quantdash

from quantdash import QuantDash
import pandas as pd
import numpy as np

# 推荐使用环境变量:
# export QUANTDASH_API_KEY="your-api-key"

qd = QuantDash()

如果你直接传 API Key,也可以:

qd = QuantDash(api_key="your-api-key")

2. 准备小市值股票池

这里假设你已经通过自己的研究流程得到一个小市值候选池。

例如:

small_cap_symbols = [
    "000001.SZ",
    "000002.SZ",
    "000004.SZ",
    "600000.SH",
    "600004.SH",
    "600006.SH",
]

实际研究时,这个列表可以更大。

关键是:

不要把“市值筛选”和“历史价格回测”混成一个不存在的 API。


3. 使用 QuantDash 批量获取前复权日 K

# 小市值轮动通常需要较长历史数据。
# 这里示范批量获取每只股票的前复权日线。

dfs = qd.klines.batch(
    small_cap_symbols,
    period="1d",
    count=1000,
    adjust="forward",
    to_dataframe=True,
    show_progress=True,
)

price_data = {}

for symbol, df in dfs.items():
    if df is None or df.empty:
        continue

    df = df.copy()
    df["trade_date"] = pd.to_datetime(df["trade_date"])
    df = df.sort_values("trade_date")

    price_data[symbol] = df[
        ["symbol", "name", "trade_date",
         "open", "high", "low", "close", "volume"]
    ]

print(f"成功获取 {len(price_data)} 只股票的历史数据")

这里用的是 QuantDash 官方文档中提供的:

qd.klines.batch(...)

以及:

adjust="forward"

4. 把股票价格整理成宽表

close_table = pd.DataFrame()

for symbol, df in price_data.items():
    temp = df[["trade_date", "close"]].copy()
    temp = temp.drop_duplicates("trade_date")
    temp = temp.set_index("trade_date")

    temp.columns = [symbol]

    if close_table.empty:
        close_table = temp
    else:
        close_table = close_table.join(temp, how="outer")

close_table = close_table.sort_index()

print(close_table.tail())

到这里,我们已经把:

多只股票的独立 K 线

变成:

日期 × 股票

的标准量化研究矩阵。


5. 构造一个简单的月度轮动框架

这里为了让逻辑足够清楚,我采用一个非常朴素的规则:

  • 每月最后一个交易日计算过去 20 个交易日收益;
  • 按收益从高到低排名;
  • 选择排名前 N 的股票;
  • 下个月持有;
  • 使用等权方式计算组合收益。

注意:

这部分是“轮动模型示例”,不是保证高收益的神奇公式。

LOOKBACK = 20
HOLD_NUM = 5

# 日收益率
daily_returns = close_table.pct_change()

# 月末交易日
month_end_dates = close_table.groupby(
    close_table.index.to_period("M")
).apply(lambda x: x.index[-1])

signals = {}

for date in month_end_dates:

    history = close_table.loc[:date].tail(LOOKBACK)

    if len(history) < LOOKBACK:
        continue

    momentum = history.iloc[-1] / history.iloc[0] - 1

    momentum = momentum.dropna()

    if momentum.empty:
        continue

    selected = momentum.sort_values(
        ascending=False
    ).head(HOLD_NUM).index.tolist()

    signals[date] = selected

for date, selected in list(signals.items())[:5]:
    print(date, selected)

6. 计算组合回测收益

portfolio_returns = []

signal_dates = list(signals.keys())

for i in range(len(signal_dates) - 1):

    signal_date = signal_dates[i]
    next_signal_date = signal_dates[i + 1]

    selected = signals[signal_date]

    if not selected:
        continue

    period_prices = close_table.loc[
        signal_date:next_signal_date,
        selected
    ]

    if period_prices.empty:
        continue

    period_returns = period_prices.pct_change()

    # 等权组合
    portfolio_daily_return = period_returns.mean(
        axis=1,
        skipna=True
    )

    portfolio_returns.append(
        portfolio_daily_return
    )

if portfolio_returns:
    strategy_returns = pd.concat(portfolio_returns)
    strategy_returns = strategy_returns[
        ~strategy_returns.index.duplicated()
    ].sort_index()

    equity = (1 + strategy_returns.fillna(0)).cumprod()

    total_return = equity.iloc[-1] - 1

    years = len(strategy_returns) / 252

    if years > 0:
        annual_return = (
            (equity.iloc[-1] ** (1 / years)) - 1
        )
    else:
        annual_return = np.nan

    max_drawdown = (
        equity / equity.cummax() - 1
    ).min()

    print("========== 回测结果 ==========")
    print(f"累计收益:{total_return:.2%}")
    print(f"年化收益:{annual_return:.2%}")
    print(f"最大回撤:{max_drawdown:.2%}")

这才是我比较喜欢的量化代码结构:

数据获取 → 信号 → 持仓 → 收益 → 指标。

每一层职责都比较清楚。


四、交易员避坑指南:小市值策略最容易在哪里翻车?

1. 千万别把未来数据带进选股

这是回测第一大坑。

比如你用月底数据选股,那么:

月底收盘之后才能知道排名。

你不能拿月底收盘价格算完排名之后,又假设自己在当天开盘价成交。

正确做法应该是:

T 日计算信号,T+1 日执行。

否则回测收益往往会漂亮得不像真的。


2. 前复权不是“万能正确答案”

QuantDash 默认支持:

adjust="forward"

前复权比例方式适合很多收益率计算场景。

但具体研究时还是要知道自己在干什么。

如果你研究的是:

  • 收益率;
  • 趋势;
  • 动量;

前复权通常比较方便。

如果你研究的是:

  • 绝对价格;
  • 分红除权影响;
  • 某些事件前后的真实价格关系;

就应该根据研究目标考虑 nonebackward 或差值复权。


3. 小市值策略不能只看收益率

这是社区里非常容易被忽略的问题。

一个回测年化 80% 的策略,如果:

  • 最大回撤 60%;
  • 换手极高;
  • 滑点巨大;
  • 小盘股成交困难;

那实盘体验可能完全不是一回事。

所以我建议至少同时看:

年化收益 + 最大回撤 + 换手率 + 单笔交易成本。


五、常见问题解答(Q&A / FAQ)

Q1:A 股小市值轮动策略可以直接用 Python 回测吗?

可以。

核心流程就是:

准备小市值候选池 → QuantDash 获取历史 K 线 → Pandas 计算排序 → 定期调仓 → 计算组合收益。

QuantDash 原生返回 Pandas DataFrame,比较适合直接接入 Python 量化研究流程。

官方 Python SDK 文档:

https://docs.quantdash.net/


Q2:QuantDash 能直接返回 A 股全部股票吗?

可以使用 A 股股票标的池:

df = qd.quotes.get(
    universes=["CN_Stock"],
    to_dataframe=True
)

不过需要注意:

“获取全市场行情”不等于“官方接口提供历史市值排名”。

如果你的策略严格依赖历史市值,需要额外准备符合研究要求的市值数据,再与 QuantDash 的 K 线进行合并。


Q3:QuantDash 为什么适合 Python 小市值策略回测?

我比较看重三个点:

  1. 支持批量 K 线;
  2. 支持前复权;
  3. 原生 Pandas DataFrame。

这样策略代码可以专注于研究,而不是天天和数据格式搏斗。


🔗 相关资源与延伸阅读

🚀 QuantDash 官网:https://quantdash.net/

📖 官方 Python SDK 文档:https://docs.quantdash.net/

⭐ GitHub 开源仓库:https://github.com/quantdash-net/QuantDash

💡 API Key:https://quantdash.net/dashboard/keys/

如果觉得这个 Python SDK 对自己的量化研究有帮助,也可以去 GitHub 项目看看,欢迎 Star / Fork。

评论