refactor: 统一价格入口 mo_data.get_price() - 22个脚本移除自拉腾讯API

所有价格获取统一走 mo_data.get_price() / get_prices_batch():
  - 优先读 live_prices(DB) → 无/过期才调 stock_quote(API) → 自动写回DB
  - 22个脚本全部替换:branch_scanner chip_factors divergence_detector
    market_screener mo_provider mofin_collect monitor_300308 300308_monitor
    multi_timeframe refresh_macro_context stale_detector stale_push_wlin
    stock_profile strategy_evaluator strategy_lifecycle strategy_review
    strategy-staleness-check technical_analysis xiaoguo_signal_consumer
    collect_evaluation_data
This commit is contained in:
知微
2026-07-08 23:54:01 +08:00
parent 0e21a3ae83
commit 9fef32413b
46 changed files with 5530 additions and 21994 deletions
+49 -37
View File
@@ -15,7 +15,7 @@ import sys
import os
from datetime import datetime, timezone
sys.path.insert(0, '/home/hmo/MoFin')
from mo_data import read_portfolio, read_decisions, read_watchlist
from mo_data import read_portfolio, read_decisions, read_watchlist, get_price, get_prices_batch
def fetch_prices(codes):
@@ -54,45 +54,14 @@ def fetch_prices(codes):
except Exception as e:
print(f"[STALE] stock_quote.py 回退: {e}", file=sys.stderr)
# 兜底:腾讯API(不应依赖,仅作为最后手段)
import urllib.request
symbols, code_map = [], {}
for c in codes:
c = str(c).strip()
p = "sh" if (len(c) == 6 and c[0] in "569") else "sz" if len(c) == 6 else "hk"
sym = f"{p}{c}"
symbols.append(sym)
code_map[sym] = c
# 兜底:mo_data.get_prices_batch
try:
req = urllib.request.Request(
f"http://qt.gtimg.cn/q={','.join(symbols)}",
headers={"User-Agent": "curl/7.81"},
)
with urllib.request.urlopen(req, timeout=10) as r:
text = r.read().decode("gbk")
raw = get_prices_batch(codes)
if raw:
return {code: (p, chg) for code, (p, chg) in raw.items()}
except Exception as e:
print(f"FETCH_FAIL (fallback): {e}", file=sys.stderr)
return {}
results = {}
for line in text.strip().split("\n"):
if "=" not in line:
continue
try:
raw = line.split("=", 1)[1].strip().strip('"').strip(";")
fld = raw.split("~")
if len(fld) < 6:
continue
sym = line.split("=", 1)[0].strip().lstrip("v_")
oc = code_map.get(sym)
if not oc:
continue
p = float(fld[3]) if fld[3] else 0
c = fld[32] if len(fld) > 32 else "0"
results[oc] = (p, c)
except (ValueError, IndexError):
continue
return results
return {}
def main():
@@ -103,6 +72,49 @@ def main():
# 只保留有买入区的条目,排除已关闭的(inactive/closed
EXCLUDED_STATUSES = ("closed", "inactive")
to_check = [d for d in decisions_list if (d.get("entry_low") is not None or d.get("entry_high") is not None) and d.get("status") not in EXCLUDED_STATUSES]
# ----- 合并 DB watchlist_stocks 自选股 -----
try:
import sqlite3
db = sqlite3.connect('/home/hmo/MoFin/data/mofin.db')
db.row_factory = sqlite3.Row
wl_rows = db.execute(
"SELECT code, name, price, entry_low, entry_high, stop_loss, analysis_json "
"FROM watchlist_stocks WHERE is_active=1 AND entry_low IS NOT NULL AND entry_high IS NOT NULL"
).fetchall()
db.close()
existing_codes = {d["code"] for d in to_check}
for row in wl_rows:
code = str(row["code"])
if code in existing_codes:
continue
entry_low = row["entry_low"]
entry_high = row["entry_high"]
if not entry_low or not entry_high or entry_low <= 0:
continue
analysis = {}
aj = row["analysis_json"]
if aj:
try:
analysis = json.loads(aj)
except (json.JSONDecodeError, TypeError):
pass
action = analysis.get("action", "") if isinstance(analysis, dict) else ""
timing_signal = analysis.get("timing_signal", "买入") if isinstance(analysis, dict) else "买入"
wl_entry = {
"code": code,
"name": row["name"] or code,
"entry_low": entry_low,
"entry_high": entry_high,
"stop_loss": row["stop_loss"],
"type": "自选策略",
"action": action,
"timing_signal": timing_signal,
}
to_check.append(wl_entry)
except Exception as e:
print(f"[WATCHLIST_MERGE FAIL] {e}", file=sys.stderr)
if not to_check:
print("[SILENT] 无需要检查的策略")
return 0