diff --git a/deploy/profile-scripts/refresh_mtf_cache.py b/deploy/profile-scripts/refresh_mtf_cache.py index f88cc5bd..88a657d8 100644 --- a/deploy/profile-scripts/refresh_mtf_cache.py +++ b/deploy/profile-scripts/refresh_mtf_cache.py @@ -1,103 +1,113 @@ -#!/usr/bin/env python3 -"""多周期缓存刷新脚本 — 在开盘前预填充K线数据 - -为所有持仓+自选股预先拉取日/周/月K线,写入 multi_tf_cache.json, -这样收盘后全量重评(regenerate_all)运行时K线数据已有缓存,无需逐个拉取。 - -运行时间:每天9:00(开盘前),no_agent模式。 -无输出 = 成功(避免每天收到无意义消息)。 -""" - -import sys -import os -import json -from datetime import datetime - -# 确保能找到 web-dashboard 模块 -sys.path.insert(0, "/home/hmo/web-dashboard") - -# 控制台UTC日志 -def log(msg): - ts = datetime.utcnow().strftime("%H:%M:%S") - print(f"[{ts}] {msg}", file=sys.stderr) - -def main(): - from mofin_db import get_conn - - # 收集所有股票代码 - codes = [] - seen = set() - - conn = get_conn() - try: - rows = conn.execute("SELECT code FROM holdings WHERE is_active=1").fetchall() - for r in rows: - code = r["code"] - if code: - codes.append(("portfolio", code)) - seen.add(code) - - rows = conn.execute("SELECT code FROM watchlist_stocks WHERE is_active=1").fetchall() - for r in rows: - code = r["code"] - if code and code not in seen: - codes.append(("watchlist", code)) - seen.add(code) - finally: - conn.close() - - # 加入指数代码(用于多周期趋势研判) - INDEXES = { - "sh000001": "上证指数", "sz399001": "深证成指", - "sz399006": "创业板指", "sh000688": "科创50", - "hkHSI": "恒生指数", "hkHSTECH": "恒生科技", - } - for idx_code in INDEXES: - if idx_code not in seen: - codes.append(("index", idx_code)) - seen.add(idx_code) - - log(f"Pre-populating multi-timeframe cache for {len(codes)} stocks...") - - # 从 DB 读取现有缓存(替代 multi_tf_cache.json) - from multi_timeframe import _load_mtf_cache, _save_mtf_cache - existing = _load_mtf_cache() - - import time - from multi_timeframe import full_multi_tf_analysis - - cached = 0 - fetched = 0 - errors = 0 - - for source, code in codes: - cached_entry = existing.get(code, {}) - updated_at = cached_entry.get("updated_at", 0) - now = time.time() - - # 检查缓存是否新鲜:日K 1小时内,周/月K 1天内 - has_daily = bool(cached_entry.get("daily")) - has_weekly = bool(cached_entry.get("weekly")) - has_monthly = bool(cached_entry.get("monthly")) - cache_fresh = (updated_at > 0 and (now - updated_at) < 3600) - - if has_daily and has_weekly and has_monthly and cache_fresh: - cached += 1 - continue - - try: - r = full_multi_tf_analysis(code) - if any(k in r for k in ["daily", "weekly", "monthly"]): - fetched += 1 - log(f" OK {code} ({source})") - else: - errors += 1 - log(f" EMPTY {code} ({source})") - except Exception as e: - errors += 1 - log(f" FAIL {code} ({source}): {e}") - - log(f"Done: {cached} cached, {fetched} fetched, {errors} errors") - -if __name__ == "__main__": - main() +#!/usr/bin/env python3 +"""多周期缓存刷新脚本 — 在开盘前预填充K线数据 + +为所有持仓+自选股预先拉取日/周/月K线,写入 multi_tf_cache.json, +这样收盘后全量重评(regenerate_all)运行时K线数据已有缓存,无需逐个拉取。 + +运行时间:每天9:00(开盘前),no_agent模式。 +无输出 = 成功(避免每天收到无意义消息)。 +""" + +import sys +import os +import json +from datetime import datetime + +# 确保能找到 web-dashboard 模块 +sys.path.insert(0, "/home/hmo/web-dashboard") + +# 控制台UTC日志 +def log(msg): + ts = datetime.utcnow().strftime("%H:%M:%S") + print(f"[{ts}] {msg}", file=sys.stderr) + +def main(): + from mofin_db import get_conn + + # 收集所有股票代码 + codes = [] + seen = set() + + conn = get_conn() + try: + rows = conn.execute("SELECT code FROM holdings WHERE is_active=1").fetchall() + for r in rows: + code = r["code"] + if code: + codes.append(("portfolio", code)) + seen.add(code) + + rows = conn.execute("SELECT code FROM watchlist_stocks WHERE is_active=1").fetchall() + for r in rows: + code = r["code"] + if code and code not in seen: + codes.append(("watchlist", code)) + seen.add(code) + finally: + conn.close() + + # 加入指数代码(用于多周期趋势研判) + INDEXES = { + "sh000001": "上证指数", "sz399001": "深证成指", + "sz399006": "创业板指", "sh000688": "科创50", + "hkHSI": "恒生指数", "hkHSTECH": "恒生科技", + } + for idx_code in INDEXES: + if idx_code not in seen: + codes.append(("index", idx_code)) + seen.add(idx_code) + + log(f"Pre-populating multi-timeframe cache for {len(codes)} stocks...") + + # 从 DB 读取现有缓存(替代 multi_tf_cache.json) + from multi_timeframe import _load_mtf_cache, _save_mtf_cache + existing = _load_mtf_cache() + + import time + from multi_timeframe import full_multi_tf_analysis + + cached = 0 + fetched = 0 + errors = 0 + + for source, code in codes: + cached_entry = existing.get(code, {}) + updated_at = cached_entry.get("updated_at", 0) + now = time.time() + + # 检查缓存是否新鲜:日K 1小时内,周/月K 1天内 + has_daily = bool(cached_entry.get("daily")) + has_weekly = bool(cached_entry.get("weekly")) + has_monthly = bool(cached_entry.get("monthly")) + cache_fresh = (updated_at > 0 and (now - updated_at) < 3600) + + if has_daily and has_weekly and has_monthly and cache_fresh: + cached += 1 + continue + + try: + r = full_multi_tf_analysis(code) + if any(k in r for k in ["daily", "weekly", "monthly"]): + fetched += 1 + log(f" OK {code} ({source})") + else: + errors += 1 + log(f" EMPTY {code} ({source})") + except Exception as e: + errors += 1 + log(f" FAIL {code} ({source}): {e}") + + # 2026-08-11 修复: 循环结束后把模块级缓存刷回 mtf_cache 表 + # (此前 full_multi_tf_analysis 只写 stocks/stock_daily 等表, + # mtf_cache 表从未被更新 -> 健康检查报 stale。flush 后写入。) + try: + from multi_timeframe import flush_mtf_cache + flush_mtf_cache() + log("mtf_cache flushed to DB") + except Exception as e: + log(f"flush_mtf_cache failed: {e}") + + log(f"Done: {cached} cached, {fetched} fetched, {errors} errors") + +if __name__ == "__main__": + main()