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

125 lines
4.3 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 -*-
"""backfill_hk_regime.py — 港股温区历史回填(一次性脚本)
背景(阶段3 港股接入):market_regime 表刚加 market 维度,港股温区只有今天
1 条,regime_tracker 的 K=5 滞回平滑需要足够历史。本脚本对 stock_daily 的
hkHSI 全历史(2014 起,约 3000 条)逐日计算温区,写入 market_regime(market='hk')。
算法:完全复用 market_regime.compute_regime 的判定逻辑(ma20/adx/roc → 三态),
只是按日期逐日循环(compute_regime 只算最新一天)。
用法:
python3 backfill_hk_regime.py # 回填港股温区全历史
python3 backfill_hk_regime.py --days 500 # 只回填最近 500 个交易日
幂等:INSERT OR REPLACE(同日同市场覆盖),可重复执行。
"""
import sqlite3
import sys
from pathlib import Path
_SCRIPT_DIR = Path(__file__).resolve().parent
import sys as _sys
_sys.path.insert(0, str(_SCRIPT_DIR))
from market_regime import calc_ma, calc_trend_strength, calc_roc, ADX_TREND_MIN
DB_PATH = Path("/home/hmo/MoFin/data/mofin.db")
INDEX_HK = "hkHSI"
LOOKBACK = 120 # 与 compute_regime 的 lookback_days 一致
def classify(above_ma20, adx):
"""与 market_regime.compute_regime 的 regime 分类完全一致"""
if above_ma20 is True and adx is not None and adx >= ADX_TREND_MIN:
return "trend_up"
elif adx is not None and adx < ADX_TREND_MIN:
return "choppy"
else:
return "trend_down"
def main():
days = None
for a in sys.argv[1:]:
if a.startswith("--days"):
days = int(a.split("=")[-1] if "=" in a else sys.argv[sys.argv.index(a) + 1])
conn = sqlite3.connect(str(DB_PATH), timeout=30)
rows = conn.execute(
"SELECT date, close, high, low FROM stock_daily WHERE code=? ORDER BY date ASC",
(INDEX_HK,)
).fetchall()
if not rows or len(rows) < 30:
print(f"{INDEX_HK} 数据不足({len(rows)} 条),无法回填")
return 1
dates = [r[0] for r in rows]
closes = [r[1] for r in rows]
highs = [r[2] for r in rows]
lows = [r[3] for r in rows]
n = len(rows)
if days:
n = min(n, days)
print(f"{INDEX_HK}{len(rows)} 条({dates[0]} ~ {dates[-1]}),回填最近 {n} 个交易日温区")
# 逐日计算(i 从第 30 天起,窗口用前 LOOKBACK 天)
written = 0
start_i = max(30, n - days) if days else 30
for i in range(start_i, n):
# 取截至 i 的最近 LOOKBACK 天
j0 = max(0, i - LOOKBACK + 1)
c = closes[j0:i + 1]
h = highs[j0:i + 1]
l = lows[j0:i + 1]
if len(c) < 30:
continue
ma20 = calc_ma(c, 20)
trend = calc_trend_strength(h, l, c)
roc = calc_roc(c)
k = len(c) - 1
close = c[k]
m20 = ma20[k]
above = (close > m20) if m20 else None
slope = None
if k >= 5 and ma20[k - 5] and ma20[k - 5] > 0 and m20:
slope = round((m20 - ma20[k - 5]) / ma20[k - 5] * 100, 3)
adx = trend[k] if k < len(trend) else None
roc_v = roc[k] if k < len(roc) else None
regime = classify(above, adx)
conn.execute("""
INSERT OR REPLACE INTO market_regime
(date, market, above_ma20, ma20_slope, roc, adx, regime, close, created_at)
VALUES (?, 'hk', ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
""", (
dates[i],
1 if above else 0,
slope,
round(roc_v, 3) if roc_v is not None else None,
round(adx, 2) if adx is not None else None,
regime,
close,
))
written += 1
if written % 500 == 0:
conn.commit()
print(f" 已回填 {written} 条(至 {dates[i]}...")
conn.commit()
# 验证
total = conn.execute(
"SELECT COUNT(*), MIN(date), MAX(date) FROM market_regime WHERE market='hk'"
).fetchone()
dist = conn.execute(
"SELECT regime, COUNT(*) FROM market_regime WHERE market='hk' GROUP BY regime"
).fetchall()
conn.close()
print(f"\n回填完成:写入 {written} 条")
print(f"market_regime 港股温区:共 {total[0]} 条,{total[1]} ~ {total[2]}")
print(f"温区分布: {dict(dist)}")
return 0
if __name__ == "__main__":
sys.exit(main())