一句话回答: KDJ 金叉(K 上穿 D)买入、死叉(K 下穿 D)卖出是最经典的随机指标策略。用 AlphaFeed 取回前复权日线算 KDJ,用「当日出信号、次日开盘成交」避免未来函数,再扣手续费,几十行就能回测出收益、胜率与最大回撤,还能加 J 值超卖过滤减少震荡假信号。
KDJ 金叉死叉人人会画,但「到底赚不赚钱」要靠回测验证。新手常犯两个错:一是用未来函数——当日收盘出信号却按当日收盘价成交(现实中你收盘才知道信号,买不到当日价);二是忽略手续费与震荡假信号,把回测做得过于乐观。正确姿势:前复权价算指标、次日开盘成交、扣费、可选超卖过滤。
from alphafeed import AlphaFeed
import pandas as pd
af = AlphaFeed() # 读取 ALPHAFEED_API_KEY
df = af.klines.get("600519.SH", period="1d", count=500,
adjust="forward", to_dataframe=True)
df = df.sort_values("trade_date").reset_index(drop=True)
def kdj(df, n=9, k_smooth=3, d_smooth=3):
low_n = df["low"].rolling(n).min()
high_n = df["high"].rolling(n).max()
rsv = (df["close"] - low_n) / (high_n - low_n) * 100
k = rsv.ewm(com=k_smooth - 1, adjust=False).mean()
d = k.ewm(com=d_smooth - 1, adjust=False).mean()
j = 3 * k - 2 * d
return k, d, j
df["K"], df["D"], df["J"] = kdj(df)# 金叉:K 上穿 D;死叉:K 下穿 D
df["golden"] = (df["K"] > df["D"]) & (df["K"].shift() <= df["D"].shift())
df["dead"] = (df["K"] < df["D"]) & (df["K"].shift() >= df["D"].shift())
# 可选:只在 J 值超卖(<20)区域附近的金叉才买,过滤高位假金叉
USE_J_FILTER = True
buy_sig = df["golden"] & (df["J"] < 20) if USE_J_FILTER else df["golden"]
# 关键:信号发生在 t 日收盘,t+1 日开盘才成交 → 持仓左移体现次日执行
df["signal"] = 0
df.loc[buy_sig, "signal"] = 1
df.loc[df["dead"], "signal"] = 0
df["position"] = df["signal"].replace(0, pd.NA).ffill().fillna(0).astype(int)
df["position"] = df["position"].shift(1).fillna(0) # 次日才持仓FEE = 0.0013 # 单边约 0.13%(佣金+印花税近似),买卖各扣
df["open_ret"] = df["open"].pct_change().shift(-1) # t 日持仓吃 t→t+1 的开盘间收益
df["trade"] = df["position"].diff().abs().fillna(0) # 换手(0→1 或 1→0)
df["strat_ret"] = df["position"] * df["open_ret"].fillna(0) - df["trade"] * FEE
df["equity"] = (1 + df["strat_ret"]).cumprod()import numpy as np
total = df["equity"].iloc[-1] - 1
days = len(df)
cagr = df["equity"].iloc[-1] ** (252 / days) - 1
dd = (df["equity"] / df["equity"].cummax() - 1).min()
trades = int(df["trade"].sum() // 2)
wins = ((df["strat_ret"] > 0) & (df["position"] == 1)).sum()
hold_days = int((df["position"] == 1).sum())
print(f"累计收益 {total:.2%} | 年化 {cagr:.2%} | 最大回撤 {dd:.2%}")
print(f"交易次数 {trades} | 持仓天数 {hold_days} | 盈利日 {wins}")关键参数:
参数 | 含义 | 本文取值 |
|---|---|---|
| KDJ 周期与平滑 | 9 / 3 / 3(经典) |
| 仅超卖区金叉才买 | J<20 |
| 单边手续费 | 0.0013 |
成交时点 | 避免未来函数 | 次日开盘 |
position.shift(1) + 次日开盘收益实现。Q:为什么不按当日收盘价成交?
A:当日收盘才算出金叉,你无法用当日收盘价买入——那是未来函数。用次日开盘价才真实可执行。
Q:KDJ 参数用 9/3/3 还是别的?
A:9/3/3 是最经典设置。周期越短越灵敏、假信号越多。建议先用经典参数,再按品种调。
Q:为什么加了 J<20 过滤反而交易变少?
A:因为只保留超卖区的金叉,过滤掉了高位金叉,交易更少但质量更高,是否更优要看回测结果。
Q:需要付费吗?
A:单只历史日线有免费额度;多标的批量回测(klines.batch + universes)需 Starter 及以上。详见定价页。
KDJ 金叉死叉回测的关键不在指标本身,而在回测的严谨性:前复权价算 KDJ、次日开盘成交避免未来函数、扣手续费、可选 J 值超卖过滤。用 AlphaFeed 取回前复权日线,几十行 Python 就能得到收益、胜率与最大回撤,客观检验这套经典信号到底值不值。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。