Appearance
行情识别:ER + ADX 判趋势 / 震荡 — 研究 notebook
价格行为判 regime:效率系数 ER(净位移/路径)+ ADX(趋势强度)。import
quant.strategy.regime,读缓存 K 线,离线可复现。是选动量(趋势态)还是反转(震荡态)的前提。仅研究学习。
1. 计算 regime(读缓存 OHLC)
python
%matplotlib inline
import os
while not os.path.exists("pyproject.toml") and os.getcwd() != "/":
os.chdir("..")
import numpy as np, pandas as pd, matplotlib, matplotlib.pyplot as plt
from matplotlib.patches import Patch
from matplotlib import font_manager
for _n in ["PingFang SC", "Hiragino Sans GB", "Arial Unicode MS", "Songti SC"]:
if _n in {f.name for f in font_manager.fontManager.ttflist}:
matplotlib.rcParams["font.sans-serif"] = [_n]; break
matplotlib.rcParams["axes.unicode_minus"] = False
from quant.strategy.regime import classify_regime # 只 import 内核
CODE, START, END = "159915.SZ", "20240801", "20250915"
df = pd.read_parquet("data/raw/momentum_rotation_etf.parquet") # 读缓存,离线
d = df[df.instrument == CODE].sort_values("datetime").set_index("datetime")
ohlc = d[["open", "high", "low", "close"]].astype(float)
reg = classify_regime(ohlc["high"], ohlc["low"], ohlc["close"], er_window=20, adx_window=14)
w = ohlc.join(reg)
w = w[(w.index >= START) & (w.index <= END)].reset_index()
print(CODE, START, "~", END, ":", len(w), "根K线; 趋势", int((w.regime=="trend").sum()),
"震荡", int((w.regime=="range").sum()), "中间", int((w.regime=="mixed").sum()))159915.SZ 20240801 ~ 20250915 : 274 根K线; 趋势 98 震荡 41 中间 135
2. K线 + 趋势(绿)/震荡(橙)阴影 + ER/ADX
判据:ER≥0.3 且 ADX≥25 → 趋势;ER≤0.2 且 ADX≤20 → 震荡;其余中间态。
python
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(14, 7), sharex=True, gridspec_kw={"height_ratios": [3, 1]})
reg = list(w.regime); i, n = 0, len(reg)
while i < n:
j = i
while j + 1 < n and reg[j + 1] == reg[i]:
j += 1
if reg[i] in ("trend", "range"):
c = "#2e7d32" if reg[i] == "trend" else "#f59e0b"
for ax in (ax1, ax2):
ax.axvspan(i - 0.5, j + 0.5, color=c, alpha=0.13, lw=0)
i = j + 1
for i, row in w.iterrows():
col = "#d32f2f" if row.close >= row.open else "#1e88e5"
ax1.vlines(i, row.low, row.high, color=col, lw=0.7)
ax1.bar(i, max(abs(row.close - row.open), 1e-9), bottom=min(row.open, row.close), width=0.7, color=col)
ax1.set_title("创业板ETF K线 + ER/ADX 识别趋势(绿)/震荡(橙)"); ax1.set_ylabel("前复权价")
ax1.grid(axis="y", alpha=0.25); ax1.set_xlim(-1, n)
ax1.legend(handles=[Patch(facecolor="#2e7d32", alpha=0.3, label="趋势(ER>=0.3且ADX>=25)"),
Patch(facecolor="#f59e0b", alpha=0.3, label="震荡(ER<=0.2且ADX<=20)"),
Patch(facecolor="#d32f2f", label="阳线"), Patch(facecolor="#1e88e5", label="阴线")],
fontsize=8, ncol=2)
ax2.plot(range(n), w.er.to_numpy(), color="#1565c0", lw=1.1); ax2.axhline(0.3, color="#1565c0", ls="--", lw=0.8, alpha=0.6)
ax2.set_ylabel("ER", color="#1565c0"); ax2.set_ylim(0, 1)
ax2b = ax2.twinx(); ax2b.plot(range(n), w.adx.to_numpy(), color="#7b1fa2", lw=1.1)
ax2b.axhline(25, color="#7b1fa2", ls="--", lw=0.8, alpha=0.6); ax2b.set_ylabel("ADX", color="#7b1fa2")
ax2.grid(axis="y", alpha=0.2)
ticks = list(range(0, n, max(n // 8, 1))); ax2.set_xticks(ticks)
ax2.set_xticklabels([w.datetime.iloc[t][:6] for t in ticks]); plt.show()
3. 结论
2024-09 那波暴涨被判趋势(绿,动量有效);盘整段判震荡(橙,该用反转/降仓)。classify_regime 可直接接进策略当 regime 门(见 mean_reversion / 动量的震荡降仓)。