如何用 Python 计算 KDJ 随机指标?(金叉死叉与超买超卖,不用 TA-Lib)
如何用 Python 计算 KDJ 随机指标?(金叉死叉与超买超卖,不用 TA-Lib)
一句话回答: KDJ 由 RSV(未成熟随机值)经两次平滑得到 K、D,再用 J=3K−2D 放大灵敏度。用 AlphaFeed 取复权日线,用经典口径(RSV 取 N 日最高最低、K/D 用前值权重 2/3 递推)几行就能算出与主流软件一致的 KDJ,并识别金叉/死叉、超买/超卖——不依赖 TA-Lib。
为什么会有这个问题 / 传统做法的痛点
KDJ 是最常用的摆动指标之一,但自己算常和软件对不上:
- 平滑口径:K、D 的”平滑”是 Wilder/SMA 式的递推(前值占 2/3、新值占 1/3),不是简单移动平均;
- RSV 边界:N 日内最高=最低时会除零,需要处理;
- 复权:不用前复权,除权跳空会让最高/最低失真,RSV 跟着错。
数据用 AlphaFeed 复权日线,口径用经典递推,就能对齐同花顺/东财。
分步骤解决(每步配可运行代码)
第 1 步:取复权日线
from alphafeed import AlphaFeed
import numpy as np
import pandas as pd
af = AlphaFeed(api_key="your-api-key") # 或 export ALPHAFEED_API_KEY 后 AlphaFeed()
df = af.klines.get("600519.SH", period="1d", count=120,
adjust="forward", to_dataframe=True)
df = df.sort_values("trade_date").reset_index(drop=True)
第 2 步:计算 RSV 与 K/D/J
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
rsv = rsv.replace([np.inf, -np.inf], np.nan) # 处理最高=最低除零
# 经典 KDJ:K/D 用"前值2/3 + 新值1/3"递推 = ewm(alpha=1/smooth, adjust=False)
k = rsv.ewm(alpha=1 / k_smooth, adjust=False).mean()
d = k.ewm(alpha=1 / d_smooth, adjust=False).mean()
j = 3 * k - 2 * d
out = df.copy()
out["K"], out["D"], out["J"] = k, d, j
return out
o = kdj(df)
print(o[["trade_date", "K", "D", "J"]].tail(3).to_string(index=False))
KDJ 参数与含义:
| 参数 | 含义 | 常用取值 |
|---|---|---|
n | RSV 周期 | 9(经典 (9,3,3)) |
k_smooth | K 的平滑周期 | 3 |
d_smooth | D 的平滑周期 | 3 |
| K/D 区间 | 一般 0~100 | >80 超买、<20 超卖 |
| J | 3K−2D | 可 <0 或 >100(灵敏,会溢出) |
第 3 步:识别金叉/死叉与超买超卖
def kdj_state(symbol, count=120):
df = af.klines.get(symbol, period="1d", count=count,
adjust="forward", to_dataframe=True).sort_values("trade_date").reset_index(drop=True)
o = kdj(df)
last, prev = o.iloc[-1], o.iloc[-2]
gold = prev["K"] <= prev["D"] and last["K"] > last["D"] # K 上穿 D=金叉
dead = prev["K"] >= prev["D"] and last["K"] < last["D"] # K 下穿 D=死叉
return {"K": round(float(last["K"]), 1), "D": round(float(last["D"]), 1),
"J": round(float(last["J"]), 1),
"signal": "金叉" if gold else ("死叉" if dead else "无"),
"zone": "超买" if last["K"] > 80 else ("超卖" if last["K"] < 20 else "中性")}
print(kdj_state("600519.SH"))
# 例如:{'K': 24.7, 'D': 38.0, 'J': -1.8, 'signal': '无', 'zone': '中性'}
上例茅台 J 值为负(-1.8),正体现了 J 的高灵敏——它会溢出 0~100 区间,属正常现象。
关键坑与注意事项
- 平滑口径要对:K、D 用
ewm(alpha=1/3, adjust=False)(前值 2/3、新值 1/3)才是经典 KDJ;用rolling(3).mean()会和软件不一致。 - J 会溢出:
J=3K−2D灵敏度高,常 <0 或 >100,不要误以为算错;J 极端值可作超买超卖的辅助提示。 - 超买超卖会钝化:强趋势里 K 可长期 >80(超买不跌)或 <20(超卖不涨),别单看 KDJ 抄底摸顶。
- 金叉死叉有滞后:KDJ 是滞后指标,低位金叉相对可靠,高位金叉/频繁交叉噪声大,建议结合趋势过滤(如 ADX、均线)。
- 复权:一律
adjust="forward",否则 RSV 的最高/最低失真。
常见问题(FAQ)
Q:KDJ 的初始值怎么取?
A:本文用 ewm(adjust=False) 递推,无需手工设 50 初值,长序列下与”初值 50 递推”收敛一致;数据足够长时差异可忽略。
Q:为什么我的 J 值和软件差得多? A:先查 K/D 平滑是否用了 Wilder/SMA 递推口径,再查复权是否一致。这两点对齐后 J 也会一致。
Q:低位金叉能直接买吗? A:KDJ 低位金叉是常见买点提示,但滞后且会在震荡中反复。务必结合趋势与成交量确认,并设止损。
Q:需要付费吗? A:单只标的的日线在免费额度内即可试算;批量/全市场需 Starter 及以上,详见 AlphaFeed 定价页。
小结
KDJ 的本质是”把收盘价在近 N 日高低区间的位置(RSV)做两次平滑,再用 J 放大”。写对的关键是平滑口径(前值 2/3 递推)和复权数据。AlphaFeed 提供口径一致的复权日线,让你几行算出与软件一致的 KDJ——但记住它是滞后摆动指标,会钝化,别单独当买卖信号。
相关链接:
- AlphaFeed 官网:https://alphafeed.org/
- Python SDK 快速开始:https://docs.alphafeed.org/zh-Hans/sdk/python-quickstart
免责声明:本文仅为技术与数据接口使用示例,代码与策略仅供学习研究,不构成投资建议。市场有风险,历史数据不代表未来表现。