feat: 温区平滑+周期记录——滞回确认K=5(数据选参:112周期/20.7天/无1天噪音), regime_cycles表, router v3读平滑温区(当前trend_down→v_oversold/s2_panic主导0.8,v_weak观察0.24,避免被2天choppy误判)

This commit is contained in:
xxm
2026-08-13 09:43:38 +08:00
parent 06fbdf5961
commit a62b05f11d
3 changed files with 211 additions and 3 deletions
+152
View File
@@ -0,0 +1,152 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""regime_tracker.py — 温区平滑跟踪 + 周期记录(2026-08-13
设计(老莫确认方向 + 数据选参):
- 实时温区判定:滞回确认 K=5(连续5天同温区才确认,滞后约5天,宁慢勿错)
数据依据:K=5 → 112周期/平均20.7天/无1天噪音(vs 原始457周期/151个1天)
- 温区周期记录:regime_cycles 表(start/end/regime/days),供策略评估归因
- 温度(rsi):不滞后,实时反映恐慌/亢奋(与温区互补:温区滞后、温度实时)
写表: regime_cycles(date_start, date_end, regime, days)
输出: market_regime_smoothed.json(当前平滑温区 + 温度)
"""
import sys
import json
import sqlite3
from pathlib import Path
from datetime import datetime
_SCRIPT_DIR = Path(__file__).resolve().parent
sys.path.insert(0, str(_SCRIPT_DIR))
sys.path.insert(0, "/home/hmo/MoFin")
DB = "/home/hmo/MoFin/data/mofin.db"
OUT = "/home/hmo/MoFin/data/market_regime_smoothed.json"
# 滞回确认天数(数据选参:K=5 甜区)
CONFIRM_DAYS = 5
def load_daily_regime():
"""读取 market_regime 逐日数据(时间正序)"""
conn = sqlite3.connect(DB, timeout=5)
rows = conn.execute(
"SELECT date, above_ma20, adx, regime FROM market_regime ORDER BY date ASC"
).fetchall()
conn.close()
return rows
def classify_day(above, adx):
"""单日温区(与 market_regime 同逻辑)"""
if above == 1 and adx is not None and adx >= 20:
return "trend_up"
if adx is not None and adx < 20:
return "choppy"
return "trend_down"
def smooth_states(rows, k=CONFIRM_DAYS):
"""滞回确认:连续 K 天同温区才确认切换。返回 (states, cycles)"""
dates = [r[0] for r in rows]
raw = [classify_day(r[1], r[2]) for r in rows]
n = len(dates)
# 状态机:current 确认态;每 K 天窗口看是否一致
states = [None] * n
current = None
for i in range(n):
if i < k - 1:
continue
window = raw[i - k + 1:i + 1]
if len(set(window)) == 1:
# 连续 K 天同温区 → 确认(切换)
current = window[0]
states[i] = current if current is not None else raw[i]
# 开头填补(前 K-1 天用原始值)
for i in range(min(k - 1, n)):
states[i] = raw[i]
# 聚合周期
cycles = []
cur = None
for i in range(n):
s = states[i]
if cur is None or s != cur["regime"]:
if cur:
cycles.append(cur)
cur = {"regime": s, "start": dates[i], "end": dates[i], "days": 1}
else:
cur["end"] = dates[i]
cur["days"] += 1
if cur:
cycles.append(cur)
return states, cycles, dates
def save_cycles(cycles):
"""写入 regime_cycles 表"""
conn = sqlite3.connect(DB, timeout=30)
conn.execute("PRAGMA busy_timeout=30000")
conn.execute("""
CREATE TABLE IF NOT EXISTS regime_cycles (
id INTEGER PRIMARY KEY AUTOINCREMENT,
regime TEXT,
start_date TEXT,
end_date TEXT,
days INTEGER,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
# 清空重建(保持与 market_regime 同步)
conn.execute("DELETE FROM regime_cycles")
for cy in cycles:
conn.execute(
"INSERT INTO regime_cycles (regime, start_date, end_date, days) VALUES (?,?,?,?)",
(cy["regime"], cy["start"], cy["end"], cy["days"])
)
conn.commit()
conn.close()
return len(cycles)
def get_temp():
"""实时温度(rsi 档位,不滞后)"""
try:
from temp_band import get_market_temp
return get_market_temp()
except Exception:
return {"band": "unknown", "rsi": None}
def main():
rows = load_daily_regime()
if len(rows) < CONFIRM_DAYS + 1:
print(f"数据不足: {len(rows)}")
return
states, cycles, dates = smooth_states(rows)
n_cycles = save_cycles(cycles)
# 当前平滑温区(最新确认态)
current_regime = states[-1]
current_date = dates[-1]
temp = get_temp()
# 最近周期列表
recent = cycles[-8:]
out = {
"current_regime": current_regime,
"current_date": current_date,
"confirm_days": CONFIRM_DAYS,
"temp": temp,
"total_cycles": n_cycles,
"recent_cycles": recent,
"updated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
}
Path(OUT).write_text(json.dumps(out, ensure_ascii=False, indent=1), encoding="utf-8")
print(f"平滑温区: {current_regime} (确认期{CONFIRM_DAYS}天, 至{current_date})")
print(f"温度: {temp.get('band')} (rsi={temp.get('rsi')})")
print(f"周期总数: {n_cycles}")
print("最近周期:")
for cy in recent:
print(f" {cy['regime']:<12} {cy['start']} ~ {cy['end']} ({cy['days']}天)")
if __name__ == "__main__":
main()
+14 -3
View File
@@ -29,7 +29,18 @@ OUT = Path("/home/hmo/MoFin/data/strategy_weights.json")
def load_regime():
"""读取最新 market_regime(三态)"""
"""读取平滑温区(regime_tracker 输出,K=5 滞回确认)。
优先用平滑结果(避免单日噪音误判);regime_tracker 不可用时回退原始 market_regime。"""
try:
p = Path("/home/hmo/MoFin/data/market_regime_smoothed.json")
if p.exists():
d = json.loads(p.read_text(encoding="utf-8"))
return {"regime": d.get("current_regime", "unknown"),
"date": d.get("current_date", ""),
"temp_band": (d.get("temp") or {}).get("band"),
"temp_rsi": (d.get("temp") or {}).get("rsi")}
except Exception:
pass
try:
from market_regime import load_market_regime
return load_market_regime()
@@ -38,9 +49,9 @@ def load_regime():
def get_temp():
"""读取市场温度(rsi 档位)"""
"""读取市场温度(rsi 档位)。regime_tracker 已含温度则直接用,否则实时算。"""
try:
from temp_band import get_market_temp, temp_multiplier
from temp_band import get_market_temp
return get_market_temp()
except Exception as e:
print(f" [router] 温度获取失败: {e}", file=sys.stderr)
@@ -205,3 +205,48 @@
| strategy_alert.py | 三振出局失效预警 |
当前:choppy×neutral → v_weak/v_lurk_v3 weight=0.8v_next4/v8.1 观察。
---
## 七、温区平滑与周期记录(2026-08-13 老莫补充设计)
### 7.1 设计原则(老莫定)
1. **策略内部不放温区门控因子**v_next4 的 行业ADX>25 是反例,应去除)——让策略在所有温区都能发信号,才能测出全温区表现
2. **策略带"适用温区"属性**——记录温区测试结果,不是内部硬门控
3. **适用温区是动态的**——常态化测试,记录策略在不同温区随时间的变化
4. **系统监控温区变化、记录温区周期**——以温区周期为锚点,触发策略周期性评估
### 7.2 平滑手段(数据选参)
**问题**:日级 regime 切换太频繁(原始 457 周期/151个1天周期),无法直接作为评估锚点。
**方案**:滞回确认(hysteresis)——连续 K 天同温区才确认切换。
**数据选参**
| K(确认天数) | 周期数 | 平均长度 | 效果 |
|---|---|---|---|
| K=1(原始) | 457 | 5.1天 | 太碎,151个1天 |
| K=3 | 177 | 13.1天 | 无1天,仍偏碎 |
| **K=5** | **112** | **20.7天** | **甜区:无1天噪音,周期合理** |
| K=10 | 49 | 47.3天 | 过度平滑,choppy 只剩391天 |
**选 K=5**:滞后约5天(宁慢勿错),事后评估与实时判断统一口径。
### 7.3 实时 vs 事后
- **实时温区**:K=5 滞回确认(滞后5天,确认才切换策略权重)
- **温度(rsi)**:不滞后(连续量,实时反映恐慌/亢奋,用于仓位乘数)
- **事后评估**:用 K=5 平滑后的周期做策略-温区归因
**温区与温度互补**:温区滞后、温度实时。例:当前平滑温区=trend_down7/17起19天)但温度=neutralrsi 58)——阴跌状态,v_oversold/s2_panic 主导(weight 0.8),v_weak 观察(0.24)。
### 7.4 落地文件
| 文件 | 功能 |
|---|---|
| regime_tracker.py | 滞回平滑(K=5+ 周期记录 regime_cycles 表 + 输出平滑温区 |
| strategy_router.py v3 | 读平滑温区 + 温度 → strategy_weights.json |
**cron 顺序**(每日):16:50 market_regime → 16:52 regime_tracker(平滑)→ 16:55 strategy_router(路由)