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:
+123
-1
@@ -13,10 +13,12 @@ JSON 文件已弃用,仅保留为历史备份。
|
||||
wl = read_watchlist() # 返回和 watchlist.json 一样的 dict 结构
|
||||
"""
|
||||
|
||||
import sqlite3, json
|
||||
import sqlite3, json, sys
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
DB_PATH = '/home/hmo/MoFin/data/mofin.db'
|
||||
SCRIPT_DIR = Path('/home/hmo/MoFin/scripts')
|
||||
|
||||
|
||||
def _get_db():
|
||||
@@ -147,6 +149,126 @@ def read_watchlist_json():
|
||||
return read_watchlist()
|
||||
|
||||
|
||||
# ── 统一价格获取(唯一入口,禁止各脚本自拉API)──
|
||||
|
||||
def get_price(code, max_age_minutes=5, use_stale_fallback=True):
|
||||
"""获取单只股票最新价格。
|
||||
|
||||
优先级: live_prices(DB) → stock_quote(API兜底)
|
||||
- live_prices 有且不超过 max_age_minutes → 直接返回
|
||||
- 没有或过期 → 调 stock_quote 拉,写回 live_prices
|
||||
- 都失败 → 返回 (None, None)
|
||||
|
||||
返回 (price, change_pct),两值都是 float 或 None。
|
||||
"""
|
||||
from mofin_db import get_price_from_db
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
# 1. 先读 DB
|
||||
try:
|
||||
db_price, db_chg = get_price_from_db(code)
|
||||
if db_price is not None and db_price > 0:
|
||||
# 检查时效性
|
||||
conn = __import__('sqlite3').connect(str(DB_PATH))
|
||||
row = conn.execute(
|
||||
"SELECT updated_at FROM live_prices WHERE code=?",
|
||||
(str(code).strip(),)
|
||||
).fetchone()
|
||||
conn.close()
|
||||
if row and row[0]:
|
||||
try:
|
||||
updated = datetime.strptime(row[0], "%Y-%m-%d %H:%M:%S")
|
||||
age = (datetime.now() - updated).total_seconds() / 60
|
||||
if age <= max_age_minutes:
|
||||
return (db_price, db_chg)
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
return (db_price, db_chg)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 2. DB 没有或过期 → 调 stock_quote
|
||||
if not use_stale_fallback:
|
||||
return (None, None)
|
||||
|
||||
try:
|
||||
import subprocess, json
|
||||
r = subprocess.run(
|
||||
[sys.executable, str(SCRIPT_DIR / "stock_quote.py"), str(code)],
|
||||
capture_output=True, text=True, timeout=15
|
||||
)
|
||||
if r.returncode == 0:
|
||||
data = json.loads(r.stdout.strip())
|
||||
price = float(data.get("price", 0))
|
||||
chg = float(data.get("change_pct", 0))
|
||||
if price > 0:
|
||||
# 写回 live_prices
|
||||
try:
|
||||
conn = __import__('sqlite3').connect(str(DB_PATH))
|
||||
conn.execute("""
|
||||
INSERT OR REPLACE INTO live_prices (code, price, change_pct, updated_at)
|
||||
VALUES (?, ?, ?, datetime('now','localtime'))
|
||||
""", (str(code).strip(), price, chg))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
except:
|
||||
pass
|
||||
return (price, chg)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return (None, None)
|
||||
|
||||
|
||||
def get_prices_batch(codes, max_age_minutes=5):
|
||||
"""批量获取价格,返回 {code: (price, change_pct)}"""
|
||||
from mofin_db import get_prices_batch_from_db
|
||||
|
||||
result = {}
|
||||
need_api = []
|
||||
|
||||
# 1. 批量读 DB
|
||||
try:
|
||||
db_prices = get_prices_batch_from_db(codes)
|
||||
for code in codes:
|
||||
cs = str(code).strip()
|
||||
if cs in db_prices:
|
||||
p, c = db_prices[cs]
|
||||
if p and p > 0:
|
||||
result[cs] = (p, c)
|
||||
continue
|
||||
need_api.append(cs)
|
||||
except:
|
||||
need_api = [str(c).strip() for c in codes]
|
||||
|
||||
# 2. 缺失的调 API
|
||||
if need_api:
|
||||
try:
|
||||
import subprocess, json
|
||||
r = subprocess.run(
|
||||
[sys.executable, str(SCRIPT_DIR / "stock_quote.py")] + need_api,
|
||||
capture_output=True, text=True, timeout=30
|
||||
)
|
||||
if r.returncode == 0:
|
||||
for line in r.stdout.strip().split("\n"):
|
||||
if not line:
|
||||
continue
|
||||
try:
|
||||
data = json.loads(line)
|
||||
code = str(data.get("code", "")).strip()
|
||||
price = float(data.get("price", 0))
|
||||
chg = float(data.get("change_pct", 0))
|
||||
if code and price > 0:
|
||||
result[code] = (price, chg)
|
||||
except:
|
||||
pass
|
||||
except:
|
||||
pass
|
||||
|
||||
return result
|
||||
|
||||
|
||||
# ── cash_log 写入 ──────────────────────────────────────────────────
|
||||
|
||||
def write_cash_log(cash_before, cash_after, frozen_before, frozen_after,
|
||||
|
||||
Reference in New Issue
Block a user