Files
MoFin/deploy/profile-scripts/migrate_regime_market.py

223 lines
8.5 KiB
Python
Raw Permalink 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 -*-
"""migrate_regime_market.py — 温区系统双市场表迁移(一次性脚本)
背景(阶段3 港股接入):温区相关 4 张表加 market 维度('a' A股 / 'hk' 港股),
A 股历史数据统一回填 market='a',与改造前语义完全一致。
迁移表与方式:
- market_regime : 重建。主键 date → (date, market)。旧数据 market='a'
- strategy_regime_perf : 重建。主键 (strategy,regime) → (strategy,regime,market)
- market_indicators : 重建。主键 date → (date, market)。旧数据 market='a'
- regime_cycles : ALTER 加列(主键 id 不变,无需重建)
幂等:每张表先查 market 列是否已存在,已存在则跳过(可重复执行)。
备份:迁移前每张表备份为 xxx_bak_20260814CREATE TABLE AS SELECT,含数据,
不含约束——回滚时数据完整可导回)。数据库另有每日备份。
用法:
python3 migrate_regime_market.py # 迁移全部 4 张表
python3 migrate_regime_market.py --check # 只检查现状,不迁移
"""
import sqlite3
import sys
from pathlib import Path
_SCRIPT_DIR = Path(__file__).resolve().parent
_MOFIN_ROOT = _SCRIPT_DIR.parent.parent
DB_PATH = Path(_MOFIN_ROOT) / "data" / "mofin.db"
BAK_SUFFIX = "_bak_20260814"
TABLES = ["market_regime", "regime_cycles", "strategy_regime_perf", "market_indicators"]
def _connect():
conn = sqlite3.connect(str(DB_PATH), timeout=30)
conn.execute("PRAGMA busy_timeout=30000")
return conn
def _table_exists(conn, name):
row = conn.execute(
"SELECT 1 FROM sqlite_master WHERE type='table' AND name=?", (name,)
).fetchone()
return row is not None
def _has_column(conn, table, col):
cols = [r[1] for r in conn.execute(f"PRAGMA table_info({table})")]
return col in cols
def _backup_table(conn, table):
"""迁移前备份:CREATE TABLE xxx_bak_20260814 AS SELECT * FROM xxx"""
bak = table + BAK_SUFFIX
conn.execute(f"DROP TABLE IF EXISTS {bak}")
conn.execute(f"CREATE TABLE {bak} AS SELECT * FROM {table}")
n = conn.execute(f"SELECT COUNT(*) FROM {bak}").fetchone()[0]
print(f" [备份] {table}{bak} ({n} 行)")
return bak
def _migrate_market_regime(conn):
"""重建:主键 (date, market),旧数据 market='a'"""
if _has_column(conn, "market_regime", "market"):
print("[跳过] market_regime 已有 market 列")
return
_backup_table(conn, "market_regime")
conn.execute("DROP TABLE IF EXISTS market_regime_new")
conn.execute("""
CREATE TABLE market_regime_new (
date TEXT NOT NULL,
market TEXT NOT NULL DEFAULT 'a',
above_ma20 INTEGER,
ma20_slope REAL,
roc REAL,
adx REAL,
regime TEXT,
close REAL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (date, market)
)
""")
conn.execute("""
INSERT INTO market_regime_new
(date, market, above_ma20, ma20_slope, roc, adx, regime, close, created_at)
SELECT date, 'a', above_ma20, ma20_slope, roc, adx, regime, close, created_at
FROM market_regime
""")
conn.execute("DROP TABLE market_regime")
conn.execute("ALTER TABLE market_regime_new RENAME TO market_regime")
n = conn.execute("SELECT COUNT(*) FROM market_regime").fetchone()[0]
print(f" [完成] market_regime: {n} 行, 主键 (date,market), 历史数据 market='a'")
def _migrate_strategy_regime_perf(conn):
"""重建:主键 (strategy, regime, market),旧数据 market='a'"""
if _has_column(conn, "strategy_regime_perf", "market"):
print("[跳过] strategy_regime_perf 已有 market 列")
return
_backup_table(conn, "strategy_regime_perf")
conn.execute("DROP TABLE IF EXISTS strategy_regime_perf_new")
conn.execute("""
CREATE TABLE strategy_regime_perf_new (
strategy TEXT,
market TEXT NOT NULL DEFAULT 'a',
regime TEXT,
trades INTEGER,
win_rate REAL,
avg_pnl REAL,
avg_hold_days REAL,
total_return_pct REAL,
cagr_pct REAL,
portfolio_max_dd_pct REAL,
capital_final REAL,
positions_taken INTEGER,
sharpe_ratio REAL,
profit_factor REAL,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (strategy, regime, market)
)
""")
conn.execute("""
INSERT INTO strategy_regime_perf_new
(strategy, market, regime, trades, win_rate, avg_pnl, avg_hold_days,
total_return_pct, cagr_pct, portfolio_max_dd_pct, capital_final,
positions_taken, sharpe_ratio, profit_factor, updated_at)
SELECT strategy, 'a', regime, trades, win_rate, avg_pnl, avg_hold_days,
total_return_pct, cagr_pct, portfolio_max_dd_pct, capital_final,
positions_taken, sharpe_ratio, profit_factor, updated_at
FROM strategy_regime_perf
""")
conn.execute("DROP TABLE strategy_regime_perf")
conn.execute("ALTER TABLE strategy_regime_perf_new RENAME TO strategy_regime_perf")
n = conn.execute("SELECT COUNT(*) FROM strategy_regime_perf").fetchone()[0]
print(f" [完成] strategy_regime_perf: {n} 行, 主键 (strategy,regime,market)")
def _migrate_market_indicators(conn):
"""重建:主键 date → (date, market),旧数据 market='a'"""
if _has_column(conn, "market_indicators", "market"):
print("[跳过] market_indicators 已有 market 列")
return
_backup_table(conn, "market_indicators")
conn.execute("DROP TABLE IF EXISTS market_indicators_new")
conn.execute("""
CREATE TABLE market_indicators_new (
date TEXT NOT NULL,
market TEXT NOT NULL DEFAULT 'a',
mkt_rsi REAL, mkt_dd60 REAL, mkt_adx REAL, mkt_above_ma20 INTEGER,
mkt_down_days INTEGER, mkt_slope REAL, mkt_roc REAL,
updated_at TEXT,
PRIMARY KEY (date, market)
)
""")
conn.execute("""
INSERT INTO market_indicators_new
(date, market, mkt_rsi, mkt_dd60, mkt_adx, mkt_above_ma20,
mkt_down_days, mkt_slope, mkt_roc, updated_at)
SELECT date, 'a', mkt_rsi, mkt_dd60, mkt_adx, mkt_above_ma20,
mkt_down_days, mkt_slope, mkt_roc, updated_at
FROM market_indicators
""")
conn.execute("DROP TABLE market_indicators")
conn.execute("ALTER TABLE market_indicators_new RENAME TO market_indicators")
n = conn.execute("SELECT COUNT(*) FROM market_indicators").fetchone()[0]
print(f" [完成] market_indicators: {n} 行, 主键 (date,market)")
def _migrate_regime_cycles(conn):
"""ALTER 加列:主键 id 不变,无需重建。market TEXT NOT NULL DEFAULT 'a'"""
if _has_column(conn, "regime_cycles", "market"):
print("[跳过] regime_cycles 已有 market 列")
return
_backup_table(conn, "regime_cycles")
conn.execute("ALTER TABLE regime_cycles ADD COLUMN market TEXT NOT NULL DEFAULT 'a'")
n = conn.execute("SELECT COUNT(*) FROM regime_cycles").fetchone()[0]
print(f" [完成] regime_cycles: {n} 行, 已加 market 列 (id 主键保留)")
def check_status(conn):
"""检查现状(--check):每张表是否存在、是否已有 market 列"""
print(f"DB: {DB_PATH}")
for t in TABLES:
if not _table_exists(conn, t):
print(f" {t:<24} 表不存在")
continue
n = conn.execute(f"SELECT COUNT(*) FROM {t}").fetchone()[0]
m = _has_column(conn, t, "market")
cols = ", ".join(r[1] for r in conn.execute(f"PRAGMA table_info({t})"))
print(f" {t:<24} {n:>7} 行 | market列: {'有' if m else '缺'} | {cols}")
def main():
if not DB_PATH.exists():
print(f"DB 不存在: {DB_PATH}")
return 1
conn = _connect()
if "--check" in sys.argv:
check_status(conn)
conn.close()
return 0
for t in TABLES:
if not _table_exists(conn, t):
print(f"[跳过] {t} 表不存在")
continue
if t == "market_regime":
_migrate_market_regime(conn)
elif t == "strategy_regime_perf":
_migrate_strategy_regime_perf(conn)
elif t == "market_indicators":
_migrate_market_indicators(conn)
elif t == "regime_cycles":
_migrate_regime_cycles(conn)
conn.commit()
print("全部完成。验证:")
check_status(conn)
conn.close()
return 0
if __name__ == "__main__":
sys.exit(main())