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
+23 -96
View File
@@ -7,114 +7,41 @@
import json
import os
import urllib.request
from datetime import datetime
from typing import Optional
from mo_data import read_portfolio, read_decisions, read_watchlist
from mo_data import read_portfolio, read_decisions, read_watchlist, get_price
DATA_DIR = "/home/hmo/web-dashboard/data"
MTF_CACHE_PATH = os.path.join(DATA_DIR, "multi_tf_cache.json")
MACRO_PATH = os.path.join(DATA_DIR, "macro_context.json")
PORTFOLIO_PATH = os.path.join(DATA_DIR, "portfolio.json")
# 腾讯API字段索引(quote批量接口)
F = {
"name": 1, "code": 2, "price": 3, "close_yest": 4, "open": 5,
"volume": 6, "outer_vol": 7, "inner_vol": 8,
"timestamp": 30, "change": 31, "change_pct": 32,
"high": 33, "low": 34,
"turnover": 37, "turnover_rate": 38, "pe": 39,
"high_limit": 41, "low_limit": 42, "amplitude": 43,
"market_cap_流通": 44, "market_cap_总": 45, "pb": 46,
"ep": 47, "es": 48, "eps": 49,
"avg_price": 51,
"sector_tag": 60, "sector": 61,
"high_52w": 67, "low_52w": 68,
}
# 港股字段偏移不同
F_HK = {
"name": 1, "code": 2, "price": 3, "close_yest": 4,
"change": 31, "change_pct": 32,
"high": 33, "low": 34, "high_limit": 48, "low_limit": 49,
"pe": 57, "pb": 58, "eps": 72, "market_cap_总": 45,
"turnover": 37,
}
def get_quote(code: str) -> dict:
"""获取腾讯API实时行情+基本面"""
raw = str(code).split("_")[0]
if len(raw) == 5 and raw.isdigit():
prefix = "hk"
fields = F_HK
elif raw.startswith("6") or raw.startswith("5"):
prefix = "sh"
fields = F
else:
prefix = "sz"
fields = F
# DB 优先
try:
from mofin_db import get_price_from_db
p, chg = get_price_from_db(raw)
if p: return {"price": p, "name": name, "code": raw, "change_pct": chg or 0}
except: pass
# Fallback: 腾讯
url = f"http://qt.gtimg.cn/q={prefix}{raw}"
try:
req = urllib.request.Request(url, headers={
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36"
})
with urllib.request.urlopen(req, timeout=5) as resp:
raw_text = resp.read().decode("gbk")
fields_raw = raw_text.split('"')[1].split("~")
except Exception as e:
return {"code": code, "error": str(e)}
def get(idx):
try:
v = fields_raw[idx].strip()
return float(v) if v else None
except (IndexError, ValueError):
return None
def get_str(idx):
try:
return fields_raw[idx].strip()
except IndexError:
return ""
is_hk = prefix == "hk"
"""获取实时行情。使用 mo_data.get_price 统一入口,保持dict格式兼容"""
price, change_pct = get_price(code)
if price is None:
return {"code": code, "error": "价格获取失败"}
result = {
"code": raw,
"name": get_str(fields["name"]),
"price": get(fields["price"]),
"change_pct": get(fields["change_pct"]),
"high": get(fields["high"]),
"low": get(fields["low"]),
"pe": get(fields["pe"]),
"pb": get(fields["pb"]),
"eps": get(fields["eps"]),
"code": code,
"name": "",
"price": price,
"change_pct": change_pct or 0,
"high": None,
"low": None,
"pe": None,
"pb": None,
"eps": None,
"market_cap": None,
"market_cap_流通": None,
"high_52w": None,
"low_52w": None,
"turnover_rate": None,
"amplitude": None,
"sector": None,
"outer_vol": None,
"inner_vol": None,
}
if is_hk:
result["market_cap"] = get(fields["market_cap_总"])
result["high_52w"] = get(fields["high_limit"])
result["low_52w"] = get(fields["low_limit"])
else:
result["market_cap"] = get(fields["market_cap_总"])
result["market_cap_流通"] = get(fields["market_cap_流通"])
result["high_52w"] = get(fields["high_52w"])
result["low_52w"] = get(fields["low_52w"])
result["turnover_rate"] = get(fields["turnover_rate"])
result["amplitude"] = get(fields["amplitude"])
result["sector"] = get_str(fields["sector"])
result["outer_vol"] = get(fields["outer_vol"])
result["inner_vol"] = get(fields["inner_vol"])
return result