Files
sanguo_vnpy_v2/scripts/data_platform/resume_5yr_watcher.py
T
claude_dev 4b83452294 fix(data): backfill硬化—重登on-error/登录超时/断路器/marker-resume+冷却续跑watcher
首跑5yr 673/5493后baostock连接死、定时重连没恢复、登录卡死=被限流。修四个硬伤:
- 重登on-error: except分支强制bs.logout+login建新socket再retry(原只sleep重试同死socket)
- 登录超时: SIGALRM给bs.login套30s闹钟,防永久挂起
- 断路器: 连续30只failed→save_progress+break+exit 2,不再假完成空跑2000票
- marker-resume: load_progress扫.baostock marker文件(非done JSON),failed无marker永远重试
- watcher: 每30min探测baostock,可登就续跑,冷却就等,exit 0则退出
9/9单测绿(Mac venv311 mock baostock)。
2026-07-13 11:56:23 +08:00

69 lines
2.3 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.
"""5yr 15min 回填冷却后自动续跑 watcher。
baostock 被限流/冷却时登录会卡死或失败。本 watcher 每 30min 探测一次 baostock
可登就启动 backfill_15min_baostockmarker-based resume 自动续跑缺口),断路器触发
或登录失败就继续等。backfill 成功(exit 0,全部缺口填完)则退出。
detached 启动:
nohup ./venv311/bin/python3 scripts/data_platform/resume_5yr_watcher.py \
> /Users/chufeng/data_cache/stock/logs/daily_update/resume_watcher.log 2>&1 &!
"""
import os
import subprocess
import time
ROOT = "/Users/chufeng/.openclaw/sanguo_projects/sanguo_vnpy_v2"
PY = os.path.join(ROOT, "venv311/bin/python3")
SCRIPT = os.path.join(ROOT, "scripts/data_platform/backfill_15min_baostock.py")
PROBE_INTERVAL = 1800 # 30min
# 探测脚本:SIGALRM 给 bs.login() 套 15s 闹钟,避免卡死
_PROBE = r'''
import signal
def _h(*a): raise TimeoutError()
signal.signal(signal.SIGALRM, _h)
signal.alarm(15)
try:
import baostock as bs
lg = bs.login()
ok = lg.error_code == "0"
try: bs.logout()
except Exception: pass
print("LOGIN_OK" if ok else "LOGIN_FAIL")
except Exception as e:
print("LOGIN_ERR", type(e).__name__, e)
finally:
signal.alarm(0)
'''
def baostock_up() -> bool:
try:
r = subprocess.run([PY, "-c", _PROBE], capture_output=True, text=True, timeout=30)
return "LOGIN_OK" in r.stdout
except Exception:
return False
def main() -> None:
round_ = 0
while True:
round_ += 1
if baostock_up():
print(f"[round {round_}] baostock 可登,启动回填(marker-resume 续跑缺口)", flush=True)
env = dict(os.environ)
env["STOCK_ROOT"] = "/Users/chufeng/data_cache/stock"
env["BS_START_DATE"] = "20210101"
rc = subprocess.call([PY, SCRIPT], env=env, cwd=ROOT)
if rc == 0:
print(f"[round {round_}] 回填完成(exit 0),退出 watcher", flush=True)
return
print(f"[round {round_}] 回填 exit={rc}(断路器/失败),等 {PROBE_INTERVAL}s 再试", flush=True)
else:
print(f"[round {round_}] baostock 不可登(冷却中),等 {PROBE_INTERVAL}s", flush=True)
time.sleep(PROBE_INTERVAL)
if __name__ == "__main__":
main()