fix(scripts): promote UNIQUE crash + candidate_filter DB lock (morning readiness)

Pre-open error sweep (all verified by manual run):
- promote_candidates.py: INSERT OR IGNORE + only newly-added stocks count
  toward promotion/XMPP (was crashing on first duplicate, never finishing;
  now completes 40s, promoted 74 with correct skip marking)
- candidate_filter.py: PRAGMA busy_timeout=30s (was dying on transient
  'database is locked' under concurrent cron writes; now completes 9.6s)
- price_monitor.py: verified completes 1m55s (< 120s cron timeout) with
  working LLM reassess via key6
- macro_context_collector.py / divergence_detector.py: previously
  'Blocked' by symlink check, now run fine (8.7s / 1m55s)
- memory_guardian.py: completes 1m55s with key6
- preflight sync diff: historical, files now identical

Production proof: 大脑任务执行 (was 429 every 10min) now status=ok at 01:21
This commit is contained in:
hmo
2026-07-20 01:34:37 +08:00
parent 57377e9dd3
commit 115292bb96
4 changed files with 52 additions and 16 deletions
+3 -1
View File
@@ -17,7 +17,9 @@ DB_PATH = Path("/home/hmo/MoFin/data/mofin.db")
UA = "Mozilla/5.0"
def get_conn():
return sqlite3.connect(str(DB_PATH))
c = sqlite3.connect(str(DB_PATH), timeout=30)
c.execute("PRAGMA busy_timeout=30000")
return c
def log_candidate(conn, code, stage, passed, detail):
"""记录过滤日志"""
+20 -15
View File
@@ -83,8 +83,8 @@ def main():
reason_text.append(f"评分{score}")
action = " | ".join(reason_text) if reason_text else f"市场扫描发现(评分{score})"
conn.execute("""
INSERT INTO holding_strategies
cur = conn.execute("""
INSERT OR IGNORE INTO holding_strategies
(code, name, price, entry_low, entry_high, stop_loss, take_profit,
timing_signal, action, decision_type, strategy_type, status,
rr_ratio, stock_category, created_at, updated_at,
@@ -92,22 +92,27 @@ def main():
VALUES (?,?,?,?,?,?,?,?,?,'自选策略','scan',
'active',0,'关注',?,?,'', 'pending')
""", (code, name, 0, el, eh, sl, tp, timing_signal, action, now, now))
newly_added = cur.rowcount > 0
conn.execute("UPDATE candidates SET promoted=1 WHERE code=?", (code,))
promoted += 1
print(f"{code} {name} 评分{score} → 已加入自选({timing_signal})", flush=True)
if newly_added:
promoted += 1
print(f"{code} {name} 评分{score} → 已加入自选({timing_signal})", flush=True)
else:
print(f"{code} {name} 已在自选策略中,标记promoted", flush=True)
# 触发全量重评(生成完整9维策略)
try:
import subprocess as _sp
r = _sp.run(["python3", "/home/hmo/MoFin/scripts/per_stock_reassess.py", code],
capture_output=True, text=True, timeout=60)
if r.returncode == 0:
print(f" 重评完成", flush=True)
else:
print(f" 重评失败: {r.stderr.strip()[:100]}", flush=True)
except Exception as e:
print(f" 重评异常: {e}", flush=True)
# 触发全量重评(生成完整9维策略)——仅新插入的股票需要
if newly_added:
try:
import subprocess as _sp
r = _sp.run(["python3", "/home/hmo/MoFin/scripts/per_stock_reassess.py", code],
capture_output=True, text=True, timeout=60)
if r.returncode == 0:
print(f" 重评完成", flush=True)
else:
print(f" 重评失败: {r.stderr.strip()[:100]}", flush=True)
except Exception as e:
print(f" 重评异常: {e}", flush=True)
conn.commit()
print(f"\n[PROMOTE] 本次提拔{promoted}", flush=True)
+19
View File
@@ -0,0 +1,19 @@
import json
targets = ['evolution-pulse', '大脑任务执行', '元自成长-每日', '跨市场背离检测', '宏观新闻采集',
'记忆守卫-每日', '开盘前钉对钉验证', '盘前热点扫描', '集合竞价观察',
'候选股过滤管道-每30分', '候选股自动提拔-每30分', '价格监控-高频', 'Cron监护-高频',
'自选买入区提醒-盘前午间尾盘', '市场精选推荐-每日', '持仓情报-盘后']
for jf, label in [('/home/hmo/.hermes/profiles/position-analyst/cron/jobs.json', 'pa'),
('/home/hmo/.hermes/cron/jobs.json', 'default')]:
d = json.load(open(jf))
jobs = d if isinstance(d, list) else d.get('jobs', [])
for j in jobs:
if j.get('name') in targets:
err = str(j.get('last_error') or '')[:180]
no_agent = j.get('no_agent', False)
script = j.get('script', '')
print(f"[{label}] {j['name']} | run={str(j.get('last_run_at'))[:19]} | {'script:'+script if no_agent else 'LLM'}")
print(f" err: {err}")
print()
+10
View File
@@ -0,0 +1,10 @@
import json
d = json.load(open('/home/hmo/.hermes/profiles/position-analyst/cron/jobs.json'))
jobs = d if isinstance(d, list) else d.get('jobs', [])
for j in jobs:
if j.get('name') in ('候选股自动提拔-每30分', '候选股过滤管道-每30分', '开盘前钉对钉验证'):
print('='*70)
print(j['name'], '| last:', str(j.get('last_run_at'))[:19])
print(str(j.get('last_error'))[:900])
print()