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

169 lines
5.6 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 -*-
"""strategy_executor.py — 策略执行调度器(2026-08-13 温区自适应核心)
核心(老莫要求):根据当前温区激活策略,调度对应的选股/买卖/重评任务。
温区自适应数据流:
regime_tracker(K=5 平滑温区) + temp_band(温度) + regime_perf(实测)
→ strategy_router(当前温区激活策略 strategy_weights.json)
→ strategy_executor(按激活策略调度对应 scanner/监控/重评)
调度逻辑:
- 读 strategy_weights.json 的 active(当前温区激活策略集合)
- 对每个激活策略,调度其对应的选股扫描器(scanner)
- 非激活策略的 scanner 不跑(温区休眠)
策略→scanner 映射:
v_weak → mr_scanner.py choppy/trend_down 超跌确认)
v_oversold → predictive_oversold_scanner.pytrend_down 预测超跌反弹)
s2_panic → s2_scanner.py trend_down 恐慌买超跌)
v_mr/v_mr_sel → mr_scanner.py (复用 v_weak 扫描器,同家族)
v_next4/v8.1 → leader_scanner.py trend_up 龙头识别)
用法:cron 每30分钟盘中跑(替代各 scanner 分散 cron
python3 strategy_executor.py
"""
import sys
import json
import subprocess
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")
WEIGHTS_FILE = "/home/hmo/MoFin/data/strategy_weights.json"
LOG = "/home/hmo/MoFin/gateway/logs/strategy_executor.log"
# 策略 → 选股扫描器映射
STRATEGY_SCANNER = {
"v_weak": "mr_scanner.py",
"v_oversold": "predictive_oversold_scanner.py",
"p_oversold": "predictive_oversold_scanner.py",
"s2_panic": "s2_scanner.py",
"v_mr": "mr_scanner.py",
"v_mr_sel": "mr_scanner.py",
"v_mr2": "mr_scanner.py",
"v_mr3": "mr_scanner.py",
"v_mr4": "mr_scanner.py",
"v_lurk_v1": "mr_scanner.py",
"v_lurk_v2": "mr_scanner.py",
"v_lurk_v3": "mr_scanner.py",
"v_next": "leader_scanner.py",
"v_next4": "leader_scanner.py",
"v8.1": "leader_scanner.py",
"v_next5": "leader_scanner.py",
"v_combo": "leader_scanner.py",
"b_td1_v3": "b_td1_v3_scanner.py",
"s2_panic_v2": "s2_panic_v2_scanner.py",
}
def log(msg):
line = f"[{datetime.now().isoformat(timespec='seconds')}] {msg}"
print(line, flush=True)
try:
Path(LOG).parent.mkdir(parents=True, exist_ok=True)
with open(LOG, "a", encoding="utf-8") as f:
f.write(line + "\n")
except Exception:
pass
def load_active():
"""读当前温区激活策略"""
try:
p = Path(WEIGHTS_FILE)
if p.exists():
d = json.loads(p.read_text(encoding="utf-8"))
return d.get("active") or [], d.get("state", "unknown")
except Exception as e:
log(f"加载 weights 失败: {e}")
return [], "unknown"
def is_trading_time():
"""盘中时段检查(9:30-11:30 / 13:00-15:00 连续竞价)"""
now = datetime.now()
t = now.strftime("%H:%M")
wd = now.weekday()
if wd >= 5:
return False
return ("09:30" <= t <= "11:30") or ("13:00" <= t <= "15:00")
def run_scanner(scanner_name):
"""执行一个扫描器(带超时防挂 + returncode 检查 + 完整 stderr 落盘)
2026-08-17 知微提单 t_b27ad65f 辅修:
1. returncode 检查——scanner 崩溃(exit!=0)记 ✗,不再误报「✓完成」
2. 超时 600→480s(每扫描器预算,避免挤占 cron harness 600s 总预算)
3. 完整 stderr 落盘(截断 200→1000 字符,便于追根因)
"""
script = _SCRIPT_DIR / scanner_name
if not script.exists():
log(f" ⚠️ {scanner_name} 不存在,跳过")
return False
log(f" ▶ 执行 {scanner_name}")
try:
r = subprocess.run(
[sys.executable, str(script)],
capture_output=True, text=True, timeout=480,
cwd=str(_SCRIPT_DIR),
)
out = (r.stdout or "").strip()[-500:] # 尾部(含完成/命中汇总)
err = (r.stderr or "").strip()[-1000:] # 完整 stderr(截断1000
if r.returncode == 0:
log(f" ✓ {scanner_name} 完成(rc=0): {out}")
if err:
log(f" ⚠️ {scanner_name} stderr(rc=0): {err}")
return True
else:
log(f" ✗ {scanner_name} 崩溃(rc={r.returncode}): {out}")
log(f" ✗ {scanner_name} stderr: {err}")
return False
except subprocess.TimeoutExpired as e:
# 超时:子进程可能孤儿化,尝试终止进程组
try:
if e.stdout or e.stderr:
pass
except Exception:
pass
log(f" ⏱ {scanner_name} 超时(480s)跳过——检查孤儿进程")
return False
except Exception as e:
log(f" ✗ {scanner_name} 异常: {e}")
return False
def main():
if not is_trading_time():
print(f"[SKIP] {datetime.now().strftime('%H:%M')} 非连续竞价时段", flush=True)
return 0
active, regime = load_active()
log(f"温区: {regime} | 激活策略: {active}")
# 去重:同一 scanner 只跑一次(多策略映射同一扫描器)
scanners = set()
for strat in active:
sc = STRATEGY_SCANNER.get(strat)
if sc:
scanners.add(sc)
log(f"调度扫描器: {sorted(scanners)}")
if not scanners:
log(" 无激活策略的扫描器(当前温区无对应选股任务)")
return 0
for sc in sorted(scanners):
run_scanner(sc)
log("调度完成")
return 0
if __name__ == "__main__":
sys.exit(main())