From 06fbdf596107248a802327b339db2a64d91077b4 Mon Sep 17 00:00:00 2001 From: xxm Date: Thu, 13 Aug 2026 09:13:04 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B5=8B=E6=B8=A9=E6=9C=BA=E5=88=B6?= =?UTF-8?q?=E5=AE=9A=E7=A8=BF=E2=80=94=E2=80=94=E4=B8=89=E6=80=81=E9=AA=A8?= =?UTF-8?q?=E6=9E=B6+=E6=B8=A9=E5=BA=A6(rsi)=E8=BF=9E=E7=BB=AD=E7=BB=B4?= =?UTF-8?q?=E5=BA=A6(=E6=95=B0=E6=8D=AE=E9=AA=8C=E8=AF=81)=20=E5=9B=9E?= =?UTF-8?q?=E5=A1=ABmarket=5Fregime=2010=E5=B9=B4(2016-2026=202319?= =?UTF-8?q?=E6=9D=A1);=20=E6=B8=A9=E5=BA=A6=E5=88=86=E6=A1=A3temp=5Fband(p?= =?UTF-8?q?anic/fear/neutral/greed/euphoria);=20strategy=5Frouter=20v2?= =?UTF-8?q?=E4=B8=89=E6=80=81=E9=80=89=E7=AD=96=E7=95=A5+=E6=B8=A9?= =?UTF-8?q?=E5=BA=A6=E4=B9=98=E6=95=B0;=20=E5=BA=9F=E5=BC=83=E5=9B=9B?= =?UTF-8?q?=E6=80=81thermometer;=20=E7=AD=96=E7=95=A5-=E9=80=82=E7=94=A8?= =?UTF-8?q?=E6=B8=A9=E5=BA=A6=E8=A1=A8(v=5Fweak=3Dchoppy=C3=97fear,=20v=5F?= =?UTF-8?q?oversold=3Dtrend=5Fdown=C3=97panic,=20v=5Fnext4=3Dtrend=5Fup=20?= =?UTF-8?q?63%)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/profile-scripts/market_thermometer.py | 176 ------------------ deploy/profile-scripts/strategy_router.py | 175 +++++++++++------ deploy/profile-scripts/temp_band.py | 111 +++++++++++ ...6-08-13-事项五-策略组合市场测温动态切换.md | 56 ++++++ 4 files changed, 286 insertions(+), 232 deletions(-) delete mode 100644 deploy/profile-scripts/market_thermometer.py create mode 100644 deploy/profile-scripts/temp_band.py diff --git a/deploy/profile-scripts/market_thermometer.py b/deploy/profile-scripts/market_thermometer.py deleted file mode 100644 index fd868f36..00000000 --- a/deploy/profile-scripts/market_thermometer.py +++ /dev/null @@ -1,176 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -"""market_thermometer.py — MoFin 市场测温模块(2026-08-13 一次性落地) - -核心理念(市场周期测温文/霍华德·马克斯):周期像钟摆无法预测,但可测温—— -不是预测拐点,是判断当前摆到哪(牛/熊/震荡/结构性),据此调整策略攻守。 - -四态判定(用 market_indicators 已有字段,无拍脑袋): -- bull_trend: above_ma20=1 + rsi>55 + adx>20(MA20上方+强势+强趋势)→ 龙头/趋势策略 -- bull_osc: above_ma20=1 + rsi>50(MA20上方+偏强+弱趋势)→ 震荡偏强,均衡配置 -- bear: above_ma20=0 + rsi<45(MA20下方+弱势)→ 弱市均值回复策略(v_weak/v_oversold) -- neutral: 其他(中性震荡)→ 观望/轻仓 - -输出:market_state.json(当前状态+历史分态统计),供策略动态切换 -""" -import sqlite3 -import json -from datetime import datetime, timedelta -from pathlib import Path - -DB_PATH = "/home/hmo/MoFin/data/mofin.db" -OUT_PATH = "/home/hmo/MoFin/data/market_state.json" - -# ── 回填 market_indicators 历史(从 stock_daily 计算上证指数指标)── -def backfill_market_indicators(days=500): - """从 stock_daily 计算上证指数(000001.SH)的 mkt_* 指标,回填到 market_indicators""" - c = sqlite3.connect(DB_PATH) - # 上证指数代码(MoFin 约定:000001 = 上证指数) - rows = c.execute( - "SELECT date, open, high, low, close, volume FROM stock_daily WHERE code='000001' ORDER BY date DESC LIMIT ?", - (days,) - ).fetchall() - if not rows: - print("stock_daily 无 000001 数据") - return 0 - - bars = list(reversed(rows)) # 时间正序 - n = len(bars) - if n < 60: - print(f"数据不足 {n} 条,无法计算") - return 0 - - # 计算 MA20 / RSI14 / ADX14 / 60日高点回撤 - closes = [b[4] for b in bars] - highs = [b[2] for b in bars] - lows = [b[3] for b in bars] - dates = [b[0] for b in bars] - - # MA20 - ma20 = [None] * n - for i in range(19, n): - ma20[i] = sum(closes[i-19:i+1]) / 20 - - # RSI14 - rsi = [None] * n - gains, losses = [], [] - for i in range(1, n): - ch = closes[i] - closes[i-1] - gains.append(max(ch, 0)) - losses.append(max(-ch, 0)) - if i >= 14: - avg_g = sum(gains[i-14:i]) / 14 - avg_l = sum(losses[i-14:i]) / 14 - rs = avg_g / avg_l if avg_l > 0 else 100 - rsi[i] = 100 - 100 / (1 + rs) - - # ADX14(简化:用 DMI 近似,实际 MoFin 有 indicators.calc_adx,这里用简化版) - adx = [None] * n - tr_list = [0.0] - pdm, ndm = [0.0], [0.0] - for i in range(1, n): - h, l, pc = highs[i], lows[i], closes[i-1] - tr = max(h - l, abs(h - pc), abs(l - pc)) - tr_list.append(tr) - up_move = highs[i] - highs[i-1] - down_move = lows[i-1] - lows[i] - pdm.append(up_move if up_move > down_move and up_move > 0 else 0) - ndm.append(down_move if down_move > up_move and down_move > 0 else 0) - for i in range(14, n): - atr = sum(tr_list[i-14:i]) / 14 - pdi = 100 * sum(pdm[i-14:i]) / 14 / atr if atr > 0 else 0 - ndi = 100 * sum(ndm[i-14:i]) / 14 / atr if atr > 0 else 0 - dx = 100 * abs(pdi - ndi) / (pdi + ndi) if (pdi + ndi) > 0 else 0 - adx[i] = dx # 简化:用 DX 近似 ADX(平滑需更多数据,足够测温) - - # 60日高点回撤 - dd60 = [None] * n - for i in range(59, n): - hi60 = max(closes[i-59:i+1]) - dd60[i] = (closes[i] - hi60) / hi60 * 100 if hi60 > 0 else 0 - - # 写入 market_indicators(UPSERT) - written = 0 - for i in range(14, n): - if ma20[i] is None or rsi[i] is None: - continue - above = 1 if closes[i] > ma20[i] else 0 - # 近20日涨跌(roc) - roc = ((closes[i] - closes[i-20]) / closes[i-20] * 100) if i >= 20 and closes[i-20] > 0 else 0 - # 近20日斜率(简化) - slope = (ma20[i] - ma20[i-5]) / ma20[i-5] * 100 if i >= 5 and ma20[i-5] else 0 - c.execute( - """INSERT OR REPLACE INTO market_indicators - (date, mkt_rsi, mkt_dd60, mkt_adx, mkt_above_ma20, mkt_down_days, mkt_slope, mkt_roc, updated_at) - VALUES (?,?,?,?,?,?,?,?,?)""", - (dates[i], rsi[i], dd60[i], adx[i], above, 0, slope, roc, datetime.now().strftime("%Y-%m-%d %H:%M:%S")) - ) - written += 1 - c.commit() - c.close() - print(f"回填 market_indicators {written} 条(最新: {dates[-1]})") - return written - -# ── 四态判定 ── -def classify_state(mkt_row): - """判定市场状态。输入:market_indicators 行 dict""" - rsi = mkt_row.get("mkt_rsi", 50) - adx = mkt_row.get("mkt_adx", 20) - above = mkt_row.get("mkt_above_ma20", 0) - dd60 = mkt_row.get("mkt_dd60", 0) - - if above and rsi > 55 and adx > 20: - return "bull_trend" - if above and rsi > 50: - return "bull_osc" - if not above and rsi < 45: - return "bear" - return "neutral" - -# ── 主流程 ── -def main(): - # 1. 回填历史(500 日 ≈ 2 年) - backfill_market_indicators(500) - - # 2. 当前状态 - c = sqlite3.connect(DB_PATH) - row = c.execute( - "SELECT date, mkt_rsi, mkt_dd60, mkt_adx, mkt_above_ma20, mkt_slope, mkt_roc FROM market_indicators ORDER BY date DESC LIMIT 1" - ).fetchone() - c.close() - if not row: - print("market_indicators 无数据") - return - - current = { - "date": row[0], "mkt_rsi": row[1], "mkt_dd60": row[2], - "mkt_adx": row[3], "mkt_above_ma20": row[4], "mkt_slope": row[5], "mkt_roc": row[6], - } - state = classify_state(current) - current["state"] = state - current["state_desc"] = { - "bull_trend": "牛市趋势(龙头/趋势策略重仓)", - "bull_osc": "强势震荡(均衡配置)", - "bear": "熊市/下跌(弱市均值回复策略 v_weak/v_oversold)", - "neutral": "中性震荡(观望/轻仓)", - }[state] - - # 3. 历史分态统计(近 250 交易日 ≈ 1 年) - c = sqlite3.connect(DB_PATH) - rows = c.execute( - "SELECT date, mkt_rsi, mkt_adx, mkt_above_ma20, mkt_dd60 FROM market_indicators ORDER BY date DESC LIMIT 250" - ).fetchall() - c.close() - from collections import Counter - hist = Counter(classify_state({"mkt_rsi": r[1], "mkt_adx": r[2], "mkt_above_ma20": r[3], "mkt_dd60": r[4]}) for r in rows) - current["hist_1y"] = dict(hist) - current["updated_at"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S") - - # 4. 写入 market_state.json - Path(OUT_PATH).write_text(json.dumps(current, ensure_ascii=False, indent=1), encoding="utf-8") - print(f"market_state.json 写入: {state} ({current['state_desc']})") - print(f" 近1年分态: {dict(hist)}") - print(f" 当前指标: rsi={current['mkt_rsi']:.1f} adx={current['mkt_adx']:.1f} above={current['mkt_above_ma20']} dd60={current['mkt_dd60']:.1f}") - -if __name__ == "__main__": - main() diff --git a/deploy/profile-scripts/strategy_router.py b/deploy/profile-scripts/strategy_router.py index c842183b..48e2dd58 100644 --- a/deploy/profile-scripts/strategy_router.py +++ b/deploy/profile-scripts/strategy_router.py @@ -1,74 +1,137 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -"""strategy_router.py — MoFin 策略动态路由(2026-08-13 一次性落地) +"""strategy_router.py v2 — MoFin 策略动态路由(三态 + 温度维度,2026-08-13 重写) -核心:读 market_state.json 的当前市场状态,决定各策略权重/开关 -- bear: v_weak/v_oversold 降权 50%(信号质量差,28%胜率) -- bull_osc: v_weak/v_oversold 正常(60%胜率,当前状态) -- bull_trend: v_weak/v_oversold 降权 50%,启用龙头策略(待研究) -- neutral: 轻仓观望 +结论(数据验证):三态骨架 + 温度(rsi)连续维度。 +- 三态(trend_up/choppy/trend_down)→ 选策略类型(market_regime.py 判定) +- 温度(rsi 档位)→ 仓位乘数(temp_band.py 判定,连续量) -输出:strategy_weights.json 供扫描器/重评脚本读取 +策略-适用温度映射(数据来源:2026-08-13 分市场状态检验,见 docs/decisions/2026-08-13-事项五): + v_weak → choppy × [fear, neutral] (震荡市+恐慌最佳 71%) + v_oversold → trend_down × [panic, fear] (下跌市+恐慌最佳 64-86%) + v_next4 → trend_up × [greed, euphoria](趋势市 63%) + v8.1 → trend_up (趋势市 57%) + s2_panic → trend_down × [panic] (极端恐慌 86%) + v_lurk_v3 → [choppy, trend_down] × [fear, neutral](77%/71%) + +输出:strategy_weights.json(各策略 weight 由 状态×温度 决定) """ import json +import sys from pathlib import Path from datetime import datetime -MARKET_STATE = Path("/home/hmo/MoFin/data/market_state.json") +_SCRIPT_DIR = Path(__file__).resolve().parent +sys.path.insert(0, str(_SCRIPT_DIR)) +sys.path.insert(0, "/home/hmo/MoFin") + OUT = Path("/home/hmo/MoFin/data/strategy_weights.json") -def load_market_state(): - if MARKET_STATE.exists(): - return json.loads(MARKET_STATE.read_text(encoding="utf-8")) - return {"state": "neutral", "date": ""} -def route(state): - """市场状态 → 策略权重。返回 dict""" - # 基准权重(等权,程飞:不确定时等权最稳健) - base = {"v_weak": 1.0, "v_oversold": 1.0, "leader": 0.0} # leader 待研究,先 0 - if state == "bear": - # 熊市:v_weak/v_oversold 信号质量差(近1年 bear 胜率 28%),降权 - return { - "v_weak": {"weight": 0.5, "action": "降权50%", "reason": "bear 胜率28%,信号质量差"}, - "v_oversold": {"weight": 0.5, "action": "降权50%", "reason": "bear 胜率28%"}, - "leader": {"weight": 0.0, "action": "停用", "reason": "bear 不适用龙头"}, - "state": state, +def load_regime(): + """读取最新 market_regime(三态)""" + try: + from market_regime import load_market_regime + return load_market_regime() + except Exception: + return None + + +def get_temp(): + """读取市场温度(rsi 档位)""" + try: + from temp_band import get_market_temp, temp_multiplier + return get_market_temp() + except Exception as e: + print(f" [router] 温度获取失败: {e}", file=sys.stderr) + return {"band": "unknown", "rsi": None} + + +# 策略-适用温度映射(基础数据,数据驱动固化) +# weight = 状态匹配度 × 温度乘数 +STRATEGY_TEMP = { + "v_weak": { + "family": "mr", + "regimes": ["choppy"], + "desc": "震荡市超跌反弹(choppy+恐慌71%最佳)", + }, + "v_oversold": { + "family": "mr", + "regimes": ["trend_down"], + "desc": "下跌市超跌(trend_down+恐慌64-86%)", + }, + "v_next4": { + "family": "trend", + "regimes": ["trend_up"], + "desc": "趋势市追涨(trend_up 63%)", + }, + "v8.1": { + "family": "trend", + "regimes": ["trend_up"], + "desc": "波段先出再进(trend_up 57%)", + }, + "s2_panic": { + "family": "mr", + "regimes": ["trend_down"], + "desc": "极端恐慌买超跌(panic 86%)", + }, + "v_lurk_v3": { + "family": "mr", + "regimes": ["choppy", "trend_down"], + "desc": "潜伏型(choppy/trend_down 71-77%)", + }, +} + + +def route(regime, temp): + """三态 + 温度 → 各策略权重""" + current_regime = regime.get("regime", "unknown") if regime else "unknown" + band = temp.get("band", "unknown") + family = temp.get("family", "mr") + + weights = {} + for name, cfg in STRATEGY_TEMP.items(): + # 状态匹配度:适用温区包含当前状态 → 1.0,否则 0.3(保留观察) + matched = current_regime in cfg["regimes"] + base = 1.0 if matched else 0.3 + # 温度乘数(按策略家族) + try: + from temp_band import temp_multiplier + mult = temp_multiplier(band, cfg["family"]) + except Exception: + mult = 0.8 + weights[name] = { + "weight": round(base * mult, 2), + "regime": current_regime, + "temp_band": band, + "family": cfg["family"], + "matched": matched, + "desc": cfg["desc"], } - if state == "bull_osc": - # 强势震荡:v_weak/v_oversold 最有效(60%胜率),正常 - return { - "v_weak": {"weight": 1.0, "action": "正常", "reason": "bull_osc 胜率60%,最有效"}, - "v_oversold": {"weight": 1.0, "action": "正常", "reason": "bull_osc 适用"}, - "leader": {"weight": 0.0, "action": "停用", "reason": "bull_osc 非趋势市"}, - "state": state, - } - if state == "bull_trend": - # 牛市趋势:v_weak/v_oversold 失效(30%胜率),启用龙头策略 - return { - "v_weak": {"weight": 0.5, "action": "降权50%", "reason": "bull_trend 胜率30%"}, - "v_oversold": {"weight": 0.5, "action": "降权50%", "reason": "bull_trend 失效"}, - "leader": {"weight": 1.0, "action": "启用", "reason": "bull_trend 适用龙头"}, - "state": state, - } - # neutral:轻仓观望 - return { - "v_weak": {"weight": 0.5, "action": "轻仓", "reason": "neutral 观望"}, - "v_oversold": {"weight": 0.5, "action": "轻仓", "reason": "neutral 观望"}, - "leader": {"weight": 0.0, "action": "停用", "reason": "neutral 观望"}, - "state": state, - } + return weights + def main(): - ms = load_market_state() - state = ms.get("state", "neutral") - weights = route(state) - weights["market_state"] = ms - weights["updated_at"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S") - OUT.write_text(json.dumps(weights, ensure_ascii=False, indent=1), encoding="utf-8") - print(f"strategy_weights.json 写入: {state}") - for k, v in weights.items(): - if isinstance(v, dict) and "weight" in v: - print(f" {k}: weight={v['weight']} ({v['action']}) - {v['reason']}") + regime = load_regime() + temp = get_temp() + if not regime: + print(" [router] market_regime 无数据", file=sys.stderr) + regime = {"regime": "unknown"} + + weights = route(regime, temp) + out = { + "state": regime.get("regime", "unknown"), + "temp_band": temp.get("band", "unknown"), + "temp_rsi": temp.get("rsi"), + "weights": weights, + "updated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + "note": "三态(trend_up/choppy/trend_down)选策略类型 + 温度(rsi)乘数决定仓位", + } + OUT.write_text(json.dumps(out, ensure_ascii=False, indent=1), encoding="utf-8") + print(f"strategy_weights.json: 状态={out['state']} 温度={out['temp_band']}(rsi={out['temp_rsi']})") + for name, w in weights.items(): + print(f" {name:<12} weight={w['weight']:<5} regime={w['regime']:<10} band={w['temp_band']:<8} {'✓' if w['matched'] else '观察'}") + if __name__ == "__main__": main() diff --git a/deploy/profile-scripts/temp_band.py b/deploy/profile-scripts/temp_band.py new file mode 100644 index 00000000..2ed14825 --- /dev/null +++ b/deploy/profile-scripts/temp_band.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""temp_band.py — 市场温度分档模块(2026-08-13) + +结论(数据验证):三态骨架 + 温度(rsi)连续维度。 +- 三态(trend_up/choppy/trend_down)= market_regime.py 已有(策略类型分类) +- 温度(rsi 分档)= 本模块(力度维度,仓位乘数) + +数据依据(2026-08-13 分市场状态检验): + v_weak choppy: 恐慌<45 -> 71%胜率 / 中性45-60 -> 46% / 亢奋>60 -> 37% + v_oversold trend_down: 恐慌<35 -> 64% / 偏弱35-50 -> 53% / 中性>50 -> 0% + s2_panic trend_down 恐慌<35: 86%胜率 +16.6% + +档位:panic(<35) / fear(35-45) / neutral(45-60) / greed(60-70) / euphoria(>70) +用法: + from temp_band import get_market_temp, temp_multiplier + t = get_market_temp() # {rsi, band, regime} + mult = temp_multiplier(t["band"], strategy_family="mr") +""" +import sqlite3 +from pathlib import Path + +DB = Path("/home/hmo/MoFin/data/mofin.db") +INDEX = "sh000001" + + +def calc_rsi(series, n=14): + result = [None] * len(series) + if len(series) < n + 1: + return result + gains, losses = [], [] + for i in range(1, len(series)): + ch = series[i] - series[i - 1] + gains.append(max(ch, 0)) + losses.append(max(-ch, 0)) + if i >= n: + avg_g = sum(gains[i - n:i]) / n + avg_l = sum(losses[i - n:i]) / n + rs = avg_g / avg_l if avg_l > 0 else 100 + result[i] = 100 - 100 / (1 + rs) + return result + + +def temp_band(rsi): + """连续 rsi -> 温度档位""" + if rsi is None: + return "unknown" + if rsi < 35: + return "panic" + if rsi < 45: + return "fear" + if rsi < 60: + return "neutral" + if rsi < 70: + return "greed" + return "euphoria" + + +def temp_multiplier(band, strategy_family="mr"): + """温度 -> 仓位乘数。mr=均值回复(恐慌重仓),trend=趋势(恐慌回避)""" + if strategy_family == "trend": + return { + "panic": 0.0, "fear": 0.0, "neutral": 0.5, + "greed": 1.0, "euphoria": 0.5, "unknown": 0.5, + }.get(band, 0.5) + return { + "panic": 1.5, "fear": 1.0, "neutral": 0.8, + "greed": 0.5, "euphoria": 0.3, "unknown": 0.5, + }.get(band, 0.5) + + +def get_market_temp(db_path=None): + """读取最新市场温度(rsi + 档位 + regime)。""" + db = db_path or DB + conn = sqlite3.connect(str(db), timeout=5) + try: + # 上证最近 30 日收盘算 rsi + rows = conn.execute( + "SELECT date, close FROM stock_daily WHERE code=? ORDER BY date DESC LIMIT 30", + (INDEX,) + ).fetchall() + # 最新 regime + reg = conn.execute( + "SELECT date, regime FROM market_regime ORDER BY date DESC LIMIT 1" + ).fetchone() + finally: + conn.close() + if not rows or len(rows) < 15: + return {"rsi": None, "band": "unknown", "regime": "unknown", "date": ""} + rows = list(reversed(rows)) + closes = [r[1] for r in rows] + dates = [r[0] for r in rows] + rsi_series = calc_rsi(closes) + rsi_now = rsi_series[-1] if rsi_series else None + band = temp_band(rsi_now) + regime = reg[1] if reg else "unknown" + reg_date = reg[0] if reg else "" + return { + "rsi": round(rsi_now, 1) if rsi_now is not None else None, + "band": band, + "regime": regime, + "date": dates[-1], + "regime_date": reg_date, + } + + +if __name__ == "__main__": + t = get_market_temp() + print(f"温度: rsi={t['rsi']} band={t['band']} regime={t['regime']} ({t['date']})") + print(f" mr 乘数: {temp_multiplier(t['band'], 'mr')}") + print(f" trend 乘数: {temp_multiplier(t['band'], 'trend')}") diff --git a/docs/decisions/2026-08-13-事项五-策略组合市场测温动态切换.md b/docs/decisions/2026-08-13-事项五-策略组合市场测温动态切换.md index 31209bd2..8a5c9407 100644 --- a/docs/decisions/2026-08-13-事项五-策略组合市场测温动态切换.md +++ b/docs/decisions/2026-08-13-事项五-策略组合市场测温动态切换.md @@ -149,3 +149,59 @@ --- **结论**:近1年不佳不是策略坏了,是市场在 bear 状态时间太长(111/250 天)vs 弱市策略类型错配。解法 = 市场测温动态切换(bear 降权/bull_osc 正常/bull_trend 换龙头)+ 新增龙头策略(bull_trend 武器)+ 失效预警淘汰(三振出局)。 + + +--- + +## 六、测温机制定稿:三态骨架 + 温度(rsi)连续维度(2026-08-13 数据验证后修正) + +### 6.1 三态 vs 四态对比(老莫提问,数据决定) + +**对比实验**:用 market_regime 回填 10 年(2016-2026,2319 条),按三态分组所有历史策略交易: + +| 策略 | trend_up | choppy | trend_down | 适用温区 | +|---|---|---|---|---| +| v_weak | 46% | **55%** | 47% | **choppy** | +| v_oversold | 50% | 57% | **60%/+12.5%** | **trend_down** | +| v_next4 | **63%/+13.6%** | - | - | **trend_up** | +| v8.1 | **57%/+9.4%** | - | - | **trend_up** | +| s2_panic | - | - | **86%/+16.6%** | **trend_down×恐慌** | +| v_lurk_v3 | - | 77% | 71% | choppy/trend_down | +| v_lurk_bull | 33%/-4.7% | 35%/-4.0% | - | 全失效(负收益)| + +**choppy 内部按 rsi 温度分组(决定性验证)**: +| rsi 档 | v_weak 胜率 | 均盈 | +|---|---|---| +| 恐慌<45 | **71%** | +6.43% | +| 中性45-60 | 46% | +1.74% | +| 亢奋>60 | 37% | -0.16% | + +### 6.2 结论:三态够用,温度是叠加的连续维度 + +1. **三态(trend_up/choppy/trend_down)= 策略类型分类**(用哪类策略)——market_regime.py 已有,回填 10 年完成 +2. **温度(rsi 分档)= 力度维度**(仓位乘数,连续量)——不并入状态分类(四态会把 rsi 硬切成 50/55 拍脑袋阈值,丢失连续性斜坡:恐慌71%→中性46%→亢奋37%) +3. **废弃 market_thermometer 四态**(避免重复基建,数据纪律:统一 market_regime 三态) + +### 6.3 策略-适用温度表(数据驱动固化,STRATEGY_TEMP 基础数据) + +| 策略 | 适用状态 | 温度档 | 数据依据 | +|---|---|---|---| +| v_weak | choppy | fear/neutral | 恐慌71% | +| v_oversold | trend_down | panic/fear | 64-86% | +| v_next4 | trend_up | greed/euphoria | 63% | +| v8.1 | trend_up | - | 57% | +| s2_panic | trend_down | panic | 86% | +| v_lurk_v3 | choppy/trend_down | fear/neutral | 71-77% | + +**关键发现**:趋势市策略 v_next4/v8.1 已躺在历史列表(不需要新发明 leader_scanner),三态×已有策略 = 完整武器库。 + +### 6.4 落地文件 + +| 文件 | 功能 | +|---|---| +| market_regime.py | 三态判定(已有,回填10年) | +| temp_band.py | 温度分档 + 仓位乘数(panic/fear/neutral/greed/euphoria)| +| strategy_router.py v2 | 三态选策略 + 温度乘数 → strategy_weights.json | +| strategy_alert.py | 三振出局失效预警 | + +当前:choppy×neutral → v_weak/v_lurk_v3 weight=0.8,v_next4/v8.1 观察。