AlphaFeed Blog

如何用 Python 回测 CCI 超买超卖反转策略?(-100 入场、+100 出场,含手续费)

策略与回测 · 代码验证于 2026-10-06

如何用 Python 回测 CCI 超买超卖反转策略?

一句话回答: CCI(顺势指标)常被当超买超卖信号——低于 −100 视为超卖、高于 +100 视为超买。本文用 AlphaFeed 取前复权日线算 CCI,严格按「信号次日开盘成交、双边扣手续费、通道/信号用前一日值避免未来函数」回测「从超卖区上穿 −100 买入、从超买区下穿 +100 卖出」的反转策略,并和买入持有对比——实测在某些标的/区间它能跑赢买入持有,但这高度依赖区间,不代表普遍有效。

为什么会有这个问题 / 传统做法的痛点

网上「CCI < −100 就抄底」的说法满天飞,但很少有人诚实地带手续费、按次日成交回测。常见错误有两个:一是用当日收盘价成交(当天收盘才算出 CCI,实际买不到),这是典型未来函数;二是忽略手续费,把高频反复进出的成本藏起来。本文把这两个坑都堵上。

分步骤解决(每步配可运行代码)

第 1 步:取前复权日线并计算 CCI

from alphafeed import AlphaFeed
import numpy as np

af = AlphaFeed(api_key="your-api-key")  # 或 export ALPHAFEED_API_KEY 后 AlphaFeed()

df = af.klines.get("600519.SH", period="1d", count=800,
                   adjust="forward", to_dataframe=True)
df = df.sort_values("trade_date").reset_index(drop=True)

def cci(df, n=14):
    tp = (df["high"] + df["low"] + df["close"]) / 3      # 典型价
    ma = tp.rolling(n).mean()
    md = tp.rolling(n).apply(lambda x: np.abs(x - x.mean()).mean(), raw=True)
    return (tp - ma) / (0.015 * md)

df["cci"] = cci(df, 14)

CCI = (典型价 − 典型价均线) ÷ (0.015 × 平均绝对偏差),±100 是经典超买超卖界。

第 2 步:次日开盘成交 + 手续费回测(避免未来函数)

df["open_next"] = df["open"].shift(-1)        # 信号次日开盘价成交
fee = 0.0013                                  # 单边约 0.13%
pos, entry, trades, rets = 0, 0.0, 0, []

for i in range(1, len(df) - 1):
    c_prev, c_now = df["cci"].iloc[i-1], df["cci"].iloc[i]
    px = df["open_next"].iloc[i]
    if np.isnan(c_prev) or np.isnan(c_now) or np.isnan(px):
        continue
    if pos == 0 and c_prev <= -100 < c_now:    # 从超卖上穿 -100 → 次日开盘买
        pos, entry = 1, px
    elif pos == 1 and c_prev >= 100 > c_now:    # 从超买下穿 +100 → 次日开盘卖
        r = px / entry * (1 - fee) ** 2 - 1     # 买卖各扣一次手续费
        rets.append(r); trades += 1; pos = 0

import pandas as pd
rets = pd.Series(rets)
total = (1 + rets).prod() - 1 if len(rets) else 0.0
bh = df["close"].iloc[-1] / df["close"].iloc[0] - 1
print("交易次数:", trades, "策略累计:", round(total*100, 2), "%",
      "买入持有:", round(bh*100, 2), "%", "胜率:", round((rets>0).mean(), 3))

关键点:判断信号用 i-1 与 i 两日的 CCI(都是已收盘的历史值),成交用 i+1 日开盘价——信号与成交严格错开,杜绝未来函数。

实测(tools/verify_new_2026-10-06.py,SDK 0.1.4):贵州茅台近 800 日,20 次交易、策略累计 +18.87%,买入持有 −18.94%,胜率 50%。这段里反转策略跑赢买入持有约 38 个百分点——但注意这是特定标的 + 特定区间的结果,换票换时间很可能反过来。

第 3 步:诚实解读

胜率仅 50% 却能赚,说明盈利靠的是「赢的那几笔幅度大于亏的」;而买入持有在这段是亏的,拉低了对比基准。不要据此断言「CCI 反转永远有效」——它在震荡市较灵、单边趋势市会被反复止损或错过大段上涨。

参数与字段说明

参数/字段含义备注
adjust="forward"前复权必须,除权缺口制造假信号
n=14CCI 周期经典 14,可调
±100超买超卖界经典阈值
open_next次日开盘成交避免未来函数的关键
fee=0.0013单边手续费买卖各扣一次

关键坑与注意事项

常见问题(FAQ)

Q:为什么用「上穿 −100」而不是「低于 −100 就买」? A:直接「低于 −100 买」会在深跌中过早进场、反复抄底。用「从超卖区上穿回 −100」要求出现企稳迹象,信号更稳。

Q:CCI 计算和 cci-commodity-channel-index 那篇重复吗? A:那篇只讲 CCI 的计算与读数,本文是完整回测(次日成交/手续费/避免未来函数/对比买入持有),侧重策略严谨性,不重复。

Q:需要付费吗? A:单只股票日线在免费额度内即可;若要对全市场批量回测 CCI 反转、横向比较,需要 klines.batch + universes(Starter 及以上),详见官网定价页。

小结

CCI 反转策略能不能赚,取决于你是否诚实地按次日开盘成交、扣手续费、避免未来函数。有了 AlphaFeed 的前复权日线,几十行就能跑出带成本的真实回测——实测某标的某区间能跑赢买入持有,但这高度依赖样本,切勿外推成「永远有效」。

相关链接:


免责声明:本文仅为技术与数据接口使用示例,代码与策略仅供学习研究,不构成投资建议。市场有风险,历史数据不代表未来表现。