feat: data-layering 消费层读live_prices替换直连qt.gtimg.cn(5脚本)
This commit is contained in:
@@ -742,7 +742,7 @@ def load_macro_context():
|
||||
|
||||
|
||||
def batch_fetch_prices(codes):
|
||||
"""获取实时价格。优先从 DB 读取(price_monitor 每 2 分钟更新),失败才拉腾讯 API。"""
|
||||
"""获取实时价格。优先从 holdings DB 读取(price_monitor 维护),兜底读 live_prices 表(2026-08-26 分层铁律)。"""
|
||||
if not codes:
|
||||
return {}
|
||||
|
||||
@@ -777,74 +777,42 @@ def batch_fetch_prices(codes):
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Fallback: 腾讯 API(仅当 DB 无数据时)
|
||||
batch_size = 15
|
||||
for batch_start in range(0, len(codes), batch_size):
|
||||
batch = codes[batch_start:batch_start + batch_size]
|
||||
symbols = []
|
||||
code_map = {}
|
||||
for raw_code in batch:
|
||||
raw_code = str(raw_code).split('_')[0]
|
||||
if not raw_code:
|
||||
continue
|
||||
if len(raw_code) == 5 and raw_code.isdigit():
|
||||
prefix = "hk"
|
||||
elif raw_code.startswith(("6", "5")):
|
||||
prefix = "sh"
|
||||
else:
|
||||
prefix = "sz"
|
||||
sym = f"{prefix}{raw_code}"
|
||||
symbols.append(sym)
|
||||
code_map[sym] = raw_code
|
||||
if not symbols:
|
||||
continue
|
||||
|
||||
url = f"http://qt.gtimg.cn/q={','.join(symbols)}"
|
||||
max_retries = 2
|
||||
for attempt in range(max_retries + 1):
|
||||
try:
|
||||
r = urllib.request.urlopen(url, timeout=10)
|
||||
text = r.read().decode("gbk")
|
||||
except Exception as e:
|
||||
if attempt < max_retries:
|
||||
# Fallback: live_prices 表(price_monitor 2分钟级实时价;2026-08-26 分层铁律:消费层不直连腾讯API)
|
||||
try:
|
||||
import sqlite3 as _sq
|
||||
_db = _sq.connect('/home/hmo/MoFin/data/mofin.db', timeout=5)
|
||||
_codes = []
|
||||
_seen = set()
|
||||
for _rc in codes:
|
||||
_rc = str(_rc).split('_')[0]
|
||||
if _rc and _rc not in _seen:
|
||||
_seen.add(_rc)
|
||||
_codes.append(_rc)
|
||||
if _codes:
|
||||
_ph = ",".join("?" * len(_codes))
|
||||
_rows = _db.execute(
|
||||
f"SELECT code, price, change_pct FROM live_prices WHERE code IN ({_ph})", _codes
|
||||
).fetchall()
|
||||
for _c, _p, _cg in _rows:
|
||||
if not _p:
|
||||
continue
|
||||
print(f" batch_fetch_prices error: {e}", file=sys.stderr)
|
||||
continue
|
||||
|
||||
for line in text.strip().split("\n"):
|
||||
line = line.strip()
|
||||
if not line or "=" not in line:
|
||||
continue
|
||||
try:
|
||||
sym = line.split("=", 1)[0].strip().lstrip("v_")
|
||||
raw_value = line.split("=", 1)[1].strip().strip('"').strip(";")
|
||||
fields = raw_value.split("~")
|
||||
if len(fields) < 35:
|
||||
continue
|
||||
orig_code = code_map.get(sym)
|
||||
if not orig_code:
|
||||
continue
|
||||
def f(i):
|
||||
try:
|
||||
return float(fields[i]) if fields[i].strip() else 0.0
|
||||
except:
|
||||
return 0.0
|
||||
price_raw = f(3)
|
||||
# 港股:腾讯 API 返回 HKD 原值,价格比较/止损止盈直接用原值
|
||||
# (仅市值/总资产汇总时由 mo_models.calc_total_assets 折算 CNY)
|
||||
all_results[orig_code] = {
|
||||
"price": price_raw, "close": f(4), "high": f(33), "low": f(34),
|
||||
"code": orig_code,
|
||||
}
|
||||
except Exception:
|
||||
continue
|
||||
break # Success - break retry loop
|
||||
_chg = _cg or 0.0
|
||||
# 港股 live_prices 价格为 HKD 原值,价格比较/止损止盈直接用原值
|
||||
# (仅市值/总资产汇总时由 mo_models.calc_total_assets 折算 CNY)
|
||||
_cl = _p / (1 + _chg / 100) if _chg else _p
|
||||
all_results[_c] = {
|
||||
"price": _p, "close": _cl, "high": _p, "low": _p,
|
||||
"code": _c,
|
||||
}
|
||||
_db.close()
|
||||
except Exception as e:
|
||||
print(f" batch_fetch_prices live_prices error: {e}", file=sys.stderr)
|
||||
|
||||
return all_results
|
||||
|
||||
|
||||
def get_price_tencent(code):
|
||||
"""获取实时价格。优先 DB(price_monitor 维护),失败才拉腾讯。港股价格存 HKD 原值。"""
|
||||
"""获取实时价格。优先 DB(holdings),兜底 live_prices 表(price_monitor 维护)。港股价格存 HKD 原值。"""
|
||||
raw_code = str(code).split('_')[0]
|
||||
if not raw_code:
|
||||
return None
|
||||
@@ -864,33 +832,24 @@ def get_price_tencent(code):
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Fallback: 腾讯 API
|
||||
# Fallback: live_prices 表(price_monitor 2分钟级实时价;2026-08-26 分层铁律:消费层不直连腾讯API)
|
||||
try:
|
||||
from mo_models import is_hk_stock
|
||||
except ImportError:
|
||||
is_hk_stock = lambda c: len(str(c).strip()) == 5 and str(c).strip().isdigit()
|
||||
try:
|
||||
if is_hk_stock(raw_code):
|
||||
prefix = "hk"
|
||||
elif raw_code.startswith("6") or raw_code.startswith("5"):
|
||||
prefix = "sh"
|
||||
else:
|
||||
prefix = "sz"
|
||||
url = f"http://qt.gtimg.cn/q={prefix}{raw_code}"
|
||||
r = urllib.request.urlopen(url, timeout=5)
|
||||
fields = r.read().decode("gbk").split('"')[1].split("~")
|
||||
def f(i):
|
||||
try:
|
||||
return float(fields[i]) if fields[i].strip() else 0.0
|
||||
except:
|
||||
return 0.0
|
||||
price = f(3)
|
||||
return {
|
||||
"price": price, "close": f(4), "high": f(33), "low": f(34),
|
||||
"code": raw_code,
|
||||
}
|
||||
import sqlite3 as _sq
|
||||
_db = _sq.connect('/home/hmo/MoFin/data/mofin.db', timeout=5)
|
||||
_row = _db.execute("SELECT price, change_pct FROM live_prices WHERE code=?", (raw_code,)).fetchone()
|
||||
_db.close()
|
||||
if _row and _row[0]:
|
||||
_chg = _row[1] or 0.0
|
||||
# 港股 live_prices 价格为 HKD 原值
|
||||
_cl = _row[0] / (1 + _chg / 100) if _chg else _row[0]
|
||||
return {
|
||||
"price": _row[0], "close": _cl, "high": _row[0], "low": _row[0],
|
||||
"code": raw_code,
|
||||
}
|
||||
print(f" get_price live_prices 无数据 {code}", file=sys.stderr)
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f" get_price error {code}: {e}", file=sys.stderr)
|
||||
print(f" get_price live_prices error {code}: {e}", file=sys.stderr)
|
||||
return None
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user