Files
MoFin/deploy/profile-scripts/regime_tracker.py
T

153 lines
4.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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()