如何用 Python 计算 Schaff 趋势周期(STC)?比 MACD 更快的趋势指标
如何用 Python 计算 Schaff 趋势周期(STC)?
一句话回答: Schaff 趋势周期(Schaff Trend Cycle, STC)由 Doug Schaff 提出,思路是「给 MACD 套上随机指标」:先算 MACD,再对 MACD 做两重随机(Stochastic)归一化 + 平滑,输出 0~100 的振荡线——STC 上穿 25 偏多、下穿 75 偏空,号称比 MACD 转向更早、比随机指标更少噪声。用 AlphaFeed 取前复权日线十几行即可实现,不需要 TA-Lib。
为什么会有这个问题 / 传统做法的痛点
MACD 反应慢、信号滞后;随机指标(KD)快但震荡市噪声大。Schaff 的想法是取两者之长:用 MACD 捕捉趋势、再用随机指标的归一化让它”快起来”并压进 0~100 的有界区间,于是既有方向性又便于设超买超卖阈值。痛点是 STC 实现较绕——要对 MACD 连做两次”随机归一化+指数平滑”,很多人卡在平滑的递归写法和除零处理上。本文给出清晰的递归实现。
分步骤解决(每步配可运行代码)
第 1 步:取前复权日线
from alphafeed import AlphaFeed
import numpy as np, pandas as pd
af = AlphaFeed(api_key="your-api-key") # 或 export ALPHAFEED_API_KEY 后 AlphaFeed()
df = af.klines.get("600519.SH", period="1d", count=400,
adjust="forward", to_dataframe=True)
df = df.sort_values("trade_date").reset_index(drop=True)
第 2 步:实现 STC(MACD → 两重随机 + 平滑)
def _smooth(series, factor=0.5): # 递归平滑:pf = pf_prev + factor*(x - pf_prev)
out = series.to_numpy(dtype=float, copy=True)
for i in range(1, len(out)):
if np.isnan(out[i]): out[i] = out[i-1]
elif not np.isnan(out[i-1]): out[i] = out[i-1] + factor*(out[i]-out[i-1])
return pd.Series(out, index=series.index)
def stc(close, fast=23, slow=50, cycle=10, factor=0.5):
macd = close.ewm(span=fast, adjust=False).mean() - close.ewm(span=slow, adjust=False).mean()
ll, hh = macd.rolling(cycle).min(), macd.rolling(cycle).max()
k = (macd - ll) / (hh - ll).replace(0, np.nan) * 100 # 第一重随机(%K)
d = _smooth(k, factor) # 平滑
ll2, hh2 = d.rolling(cycle).min(), d.rolling(cycle).max()
kd = (d - ll2) / (hh2 - ll2).replace(0, np.nan) * 100 # 第二重随机
return _smooth(kd, factor) # 再平滑 = STC
df["stc"] = stc(df["close"])
print(df[["trade_date", "stc"]].tail())
实测(
tools/verify_new_2026-10-15.py,SDK 0.1.4):贵州茅台近 400 日 STC 尾值 ≈ 93.75(超买区),区间 0 ~ 100——STC 在高位说明当前处于上行周期的强势段。
第 3 步:超买超卖 + 上下穿信号
cross_up = (df["stc"].shift(1) < 25) & (df["stc"] >= 25) # 自下穿越 25 → 偏多
cross_down = (df["stc"].shift(1) > 75) & (df["stc"] <= 75) # 自上穿越 75 → 偏空
print("最近 STC 上穿 25:", df.loc[cross_up, "trade_date"].tail(1).tolist())
常用法:STC 从 <25 回升上穿 25 视为转多,从 >75 回落下穿 75 视为转空;STC 持续贴近 100/0 表示强趋势。
参数与字段说明
| 参数/概念 | 含义 | 备注 |
|---|---|---|
adjust="forward" | 前复权 | 必须,除权缺口会扭曲 MACD |
fast/slow | 23/50 | STC 的 MACD 快慢线(比常规 12/26 长) |
cycle | 10 | 两次随机归一化的回看周期 |
factor | 0.5 | 递归平滑系数 |
| STC | 0~100 | <25 超卖 / >75 超买 |
| 上穿 25 / 下穿 75 | 转多/转空 | 经典信号 |
关键坑与注意事项
- 参数口径多样:STC 常见参数 (23,50,10) 或 (12,26,10),平滑系数、循环长度各实现不同;对比软件前先统一参数。
- “更快”的代价是更吵:STC 比 MACD 转向早,但在震荡/无趋势时会频繁在 0/100 间跳动,假信号不少,需配合趋势过滤。
- 除零与 NaN:随机归一化分母
hh−ll可能为 0(平台期),必须replace(0, nan)并在平滑里前向填充。 - 递归平滑:STC 两步都带指数平滑,依赖前值,必须循环实现,前段不稳定。
- 前复权必须:MACD 基于价格,除权缺口会制造假趋势。
- 有界≠有效:压进 0~100 便于设阈值,但不代表信号更准,仍需回测验证。
常见问题(FAQ)
Q:STC 真的比 MACD 好吗? A:STC 通常转向更早(因为做了随机归一化),在趋势行情里体验更好;但”更早”也意味着在震荡市更容易骗线。没有免费的午餐,建议结合环境过滤(如震荡指数)使用。
Q:STC 和 KDJ、MACD 的关系? A:STC = 对 MACD 的结果再跑两遍随机指标并平滑。可以理解为”趋势(MACD) + 快速归一化(Stochastic)“的合体,兼具两者特征。
Q:需要付费吗?
A:单只股票日线在免费额度内即可;若要对全市场批量扫 STC 上穿 25 的票,需要 klines.batch + universes(Starter 及以上),详见官网定价页。
小结
Schaff 趋势周期把 MACD 再做两重随机归一化与平滑,得到 0~100、转向更快的趋势振荡线,上穿 25 偏多、下穿 75 偏空。用 AlphaFeed 的前复权日线十几行即可实现——记住参数口径要统一、处理除零与递归平滑、“更快”在震荡市也更易骗线。
相关链接:
- AlphaFeed 官网:https://alphafeed.org/
- Python SDK 快速开始:https://docs.alphafeed.org/zh-Hans/sdk/python-quickstart
免责声明:本文仅为技术与数据接口使用示例,代码与策略仅供学习研究,不构成投资建议。市场有风险,历史数据不代表未来表现。