一句话结论:StockAPI 把 9 类技术指标做成了独立接口,入参几乎一致(**
code** +startDate+endDate+calculationCycle**),只是周期参数不同。你不需要自己实现任何公式,只要记住"哪个接口对应哪个字段名",20 行代码就能把 7 个指标拼成一张宽表。关键坑只有一个:指标值是字符串,必须先转数值**。
本文的行情数据来自 StockAPI 的公开 HTTP 接口(官方文档共 84 个接口,覆盖行情、技术指标、股池、资金流、游资、实时与回测)。调用方式统一:GET 请求、URL 参数 token 鉴权、返回 JSON,成功标识为 code=20000 与 msg=success;行情、指标、股池类接口限频多为 40 次/秒,交易日 15:30 后陆续更新。下文出现的接口地址、字段名、限频数值与更新时间,均按该文档原文引用,未作改写。
指标 | 路径标识 | 特有参数 | 返回字段 | 默认周期 |
|---|---|---|---|---|
MACD |
|
|
| 9 / 26 / 12 |
KDJ |
|
|
| 9 / 3 / 3 |
RSI |
|
|
| 6 / 12 / 24 |
BOLL |
|
|
| 2 / 26 |
BIAS |
|
|
| 6 / 12 / 24 |
WR |
|
|
| 10 / 6 |
CCI |
|
|
| 14 |
MA |
|
|
| 自定义 |
神奇九转 |
|
|
| — |
公共参数:code(股票/板块/概念)、startDate、endDate、calculationCycle(100 日 / 101 周 / 102 月)。全部 GET,限频 40 次/秒。
所有指标接口的调用方式完全一样,唯一差别是路径和特有参数。所以先写两个函数,后面每个指标只需改一行。
# pip install requests pandas
import os
import pandas as pd
import requests
TOKEN = os.getenv("STOCKAPI_TOKEN", "你的token")
BASE = "https://www.stockapi.com.cn"
def call(path: str, **params):
params["token"] = TOKEN
r = requests.get(BASE + path, params=params, timeout=15).json()
if r.get("code") != 20000:
raise RuntimeError(f"{r.get('code')} {r.get('msg')}")
return r["data"]
def indicator(path: str, code: str, start: str, end: str,
cycle_type: str = "100", **extra) -> pd.DataFrame:
"""取任意指标,统一转成 date 索引的数值表。"""
data = call(path, code=code, startDate=start, endDate=end,
calculationCycle=cycle_type, **extra)
df = pd.DataFrame(data) if isinstance(data, list) else pd.DataFrame(data)
if "date" in df.columns:
df["date"] = pd.to_datetime(df["date"])
df = df.set_index("date").sort_index()
df = df.drop(columns=[c for c in ("api_code",) if c in df.columns])
return df.apply(pd.to_numeric, errors="coerce") # 关键:字符串转数值df.apply(pd.to_numeric, errors="coerce") 这一行是整个骨架里最重要的。指标接口返回的 "k": "74.026411185765" 是字符串,不转数值的话 df["k"] > 70 会按字典序比较,"9" > "70" 结果为真——这类错误不报异常,只会让你得出相反的结论。
macd = indicator("/v1/quota/macd2", "600004", "2025-10-01", "2026-10-27",
cycle="9", longCycle="26", shortCycle="12")
print(macd.tail())字段 | 含义 | 怎么用 |
|---|---|---|
| 快线(12EMA − 26EMA) | 与 DEA 交叉形成金叉/死叉 |
| 慢线(DIFF 的 9 日 EMA) | 作为信号线 |
| 柱状值(通常为 2×(DIFF−DEA)) | 柱体由负转正是动能反转信号 |
不用自己算的最大好处是口径一致。自己实现时 EMA 的初始值处理方式不同,会导致前几十根 K 线的值有偏移,于是出现"我这边金叉、软件里还没金叉"的尴尬。
macd["golden"] = (macd["DIFF"] > macd["DEA"]) & (macd["DIFF"].shift() <= macd["DEA"].shift())
print(macd[macd["golden"]].tail(5))k d j 三者的关系kdj = indicator("/v1/quota/kdj2", "600004", "2025-10-01", "2026-10-27",
cycle="9", cycle1="3", cycle2="3")字段 | 含义 | 常见阈值 |
|---|---|---|
| 随机指标快线 | < 20 超卖,> 80 超买 |
| 慢线 | 与 k 交叉产生信号 |
| 3K − 2D 的放大值 | 波动最大,> 100 或 < 0 表示极端 |
j 值会突破 0 和 100 的边界,这是它的设计特性(3K−2D),不是异常值。很多人误以为 j 出了 bug,其实这是正常的超买超卖放大。
kdj["low_turn"] = (kdj["k"] > kdj["d"]) & (kdj["k"].shift() <= kdj["d"].shift()) & (kdj["d"] < 30)rsi = indicator("/v1/quota/rsi2", "600004", "2025-10-01", "2026-10-27",
cycle1="6", cycle2="12", cycle3="24")rsi1(6日)最灵敏,rsi2(12日)中性,rsi3(24日)最稳。三线同时低于 30 是较强的超卖信号,单线低于 30 意义有限——短周期 RSI 在震荡市里会反复穿越 30。
UPPER / MID / LOWER 的正确读法boll = indicator("/v1/quota/boll2", "600004", "2025-10-01", "2026-10-27",
bandwidth="2", cycle="26")字段 | 含义 | 注意 |
|---|---|---|
| 26 日中轨(通常是 MA26) | 不是 MA20,别按习惯填 |
| 上轨 = MID + 2×标准差 |
|
| 下轨 = MID − 2×标准差 | — |
关键细节:cycle 默认 26,而很多人习惯用 20。想用 MA20 口径就要显式传 cycle="20",否则和看盘软件对不上。
boll["bandwidth_pct"] = (boll["UPPER"] - boll["LOWER"]) / boll["MID"]
squeeze = boll["bandwidth_pct"] < boll["bandwidth_pct"].rolling(60).quantile(0.2)
print(f"当前是否处于布林收口(近60日最窄20%):{bool(squeeze.iloc[-1])}")布林带宽(bandwidth) 是上下轨之差除以中轨,用来衡量波动率。带宽收缩到历史低位(俗称"收口")通常预示方向选择,这是 BOLL 最有价值的用法,比"触上轨就卖"实用得多。
bias = indicator("/v1/quota/bias2", "600004", "2025-10-01", "2026-10-27",
cycle1="6", cycle2="12", cycle3="24")
wr = indicator("/v1/quota/wr2", "600004", "2025-10-01", "2026-10-27",
cycle1="10", cycle2="6")
cci = indicator("/v1/quota/cci2", "600004", "2025-10-01", "2026-10-27", cycle="14")指标 | 字段 | 方向 | 极值参考 |
|---|---|---|---|
BIAS 乖离率 |
| 正值过大 = 偏离均线太远 | ±5%(6日)、±10%(24日) |
WR 威廉指标 |
| 数值越小越强(0–100,反向) | < 20 超买,> 80 超卖 |
CCI 顺势指标 |
| 突破 ±100 为趋势信号 | 100 强势,< −100 弱势 |
WR 最容易被误读:它和 KDJ 的 RSI 方向相反——WR 接近 0 表示价格在区间高位,接近 100 表示在低位。如果你按"低于 20 就买"的直觉操作,会完全做反。
ma = indicator("/v1/quota/ma2", "600004", "2025-10-01", "2026-10-27",
ma="5,10,20,60")
print(ma.columns.tolist()) # ['ma5', 'ma10', 'ma20', 'ma60']两个注意点:
ma 的文档类型标为 int,但实际必须传字符串 "5,10,20",且分隔符必须是英文逗号(中文逗号会解析失败)。ma5、date 各是数组),而 BOLL/BIAS 是行式。上面的 indicator() 已经兼容两种,但如果你自己写解析,这里会踩坑。nine = pd.DataFrame(call("/v1/quota/nineTurn", code="600004",
date="2026-10-27", calculationCycle="100"))
print(nine[["date", "lowNine", "highNine"]])神奇九转(TD Sequential 的简化版) 是判断趋势衰竭的计数指标。接口直接返回两个布尔标记:
字段 | 值 | 含义 |
|---|---|---|
| 1 | 出现低位九转,下跌动能可能衰竭 |
| 1 | 出现高位九转,上涨动能可能衰竭 |
它不入参 startDate/endDate,只接受单日 date,所以批量取要循环。这也是它和其余指标最大的结构差异。
def build_panel(code: str, start: str, end: str) -> pd.DataFrame:
args = dict(code=code, start=start, end=end, cycle_type="100")
frames = {
"macd": indicator("/v1/quota/macd2", cycle="9", longCycle="26",
shortCycle="12", **args),
"kdj": indicator("/v1/quota/kdj2", cycle="9", cycle1="3",
cycle2="3", **args),
"rsi": indicator("/v1/quota/rsi2", cycle1="6", cycle2="12",
cycle3="24", **args),
"boll": indicator("/v1/quota/boll2", bandwidth="2", cycle="26", **args),
}
panel = pd.concat(frames, axis=1) # 多级列名
panel.columns = ["_".join(c).strip("_") for c in panel.columns]
return panel.dropna(how="all")
panel = build_panel("600004", "2025-10-01", "2026-10-27")
# 共振条件:MACD 金叉 + KDJ 低位金叉 + RSI 未超买 + 价格不破布林中轨
panel["sig"] = (
(panel["macd_DIFF"] > panel["macd_DEA"])
& (panel["macd_DIFF"].shift() <= panel["macd_DEA"].shift())
& (panel["kdj_k"] > panel["kdj_d"])
& (panel["kdj_d"] < 40)
& (panel["rsi_rsi1"] < 65)
)
print(panel[panel["sig"]].tail(8))支持。calculationCycle 传 101 取周线指标、102 取月线指标,100 是日线。同一套参数,改一个值就切换周期,不需要另外的接口。
能。code 支持板块代码(如 BK0733)和概念,文档明确写"可传股票、板块、概念代码"。这意味着行业轮动的技术面判断不需要自己合成。
三个可能:① 复权口径(接口是前复权,软件可能后复权);② 起始日期太短,EMA 还没收敛(建议至少取 120 个交易日);③ 数据精度截断,指标值返回的是长小数,软件可能只保留 2 位。
字符串。文档示例明确是 "k": "74.026411185765" 这种带引号的形式。必须 pd.to_numeric 转换后再做比较和运算。
接口接受 startDate/endDate,没有明确上限,但返回体大小会随范围增长。建议单次不超过 3 年,需要更长历史就分段拉取后拼接。
技术指标的接口化,省掉的不是那几十行公式,而是口径对齐的成本。当你看到的 k、d、j 和看盘软件完全一致时,讨论"这个金叉该不该进"才有意义。先把 7 个指标拼成一张宽表,再谈策略——这一步没有捷径,但有模板。
本文全部参数、默认值、返回字段整理自 StockAPI 公开接口文档;指标与日期(
100/101/102)及限频 40 次/秒同文档标注。代码基于 Python 3.10 + pandas 2.x,替换 token 即可运行。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。