e8653acd37
- 11个不一致cron脚本同步(MoFin→profile) - 4个游离代码收入MoFin(300308_monitor/monitor_300308/refresh_mtf_cache/strategy-staleness-check) - 6个关键依赖部署到profile(mo_models/mo_config/mo_provider/hk_rate/data_governance/data_validate)
99 lines
3.2 KiB
Python
Executable File
99 lines
3.2 KiB
Python
Executable File
#!/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 strategy_lifecycle import safe_json_load, PORTFOLIO_PATH, WATCHLIST_PATH
|
||
|
||
# 收集所有股票代码
|
||
codes = []
|
||
for item in safe_json_load(PORTFOLIO_PATH, {}).get("holdings", []):
|
||
code = item.get("code", "")
|
||
if code:
|
||
codes.append(("portfolio", code))
|
||
seen = set(c[1] for c in codes)
|
||
for item in safe_json_load(WATCHLIST_PATH, {}).get("stocks", []):
|
||
code = item.get("code", "")
|
||
if code and code not in seen:
|
||
codes.append(("watchlist", code))
|
||
seen.add(code)
|
||
|
||
# 加入指数代码(用于多周期趋势研判)
|
||
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...")
|
||
|
||
# 检查当前缓存,只更新需要更新的
|
||
mtf_cache_path = "/home/hmo/web-dashboard/data/multi_tf_cache.json"
|
||
try:
|
||
with open(mtf_cache_path) as f:
|
||
existing = json.load(f)
|
||
except (FileNotFoundError, json.JSONDecodeError):
|
||
existing = {}
|
||
|
||
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()
|