refactor: 消费者切 SQLite 优先读取

切换策略: SQLite 优先 → 失败回退 JSON

price_events (100%覆盖):
- strategy_feedback.py: run() 优先 query_price_events()
- system_health_check.py: 优先 query_price_events() + query_price_events_by_date()

stock_sector_map (100%覆盖):
- strategy_lifecycle.py: load_stock_sector_map() 优先 stock_sectors 表

market.json (85%覆盖):
- strategy_lifecycle.py: load_market_context() 优先 query_latest_market()
- market_insight.py: generate() 优先 query_latest_market()

portfolio.json + watchlist.json (70%覆盖):
- strategy_lifecycle.py: regenerate_all() 优先 query_holdings() + query_watchlist()
- server.py: /api/portfolio, /api/watchlist, /api/overview, /api/market 优先 SQLite

所有改动保留 JSON 回退路径,SQLite 不可用时自动降级
This commit is contained in:
hmo
2026-06-20 17:50:15 +08:00
parent 1610f184a0
commit 25f8c6ec67
5 changed files with 186 additions and 49 deletions
+58 -13
View File
@@ -77,11 +77,24 @@ def load_stock_sector_map():
stock_sector_map.json 格式: {code: [sector1, sector2, ...]}
跳过 _note, _created_at 等元数据键。
"""
# 优先从 SQLite 读取
try:
from mofin_db import get_conn, query_sector_stocks
conn = get_conn()
# 从 stock_sectors 表反向构建 code→[sectors] 映射
rows = conn.execute("SELECT code, sector_name FROM stock_sectors ORDER BY code").fetchall()
conn.close()
code_to_sectors = {}
for code, sector in rows:
if code not in code_to_sectors:
code_to_sectors[code] = []
code_to_sectors[code].append(sector)
return code_to_sectors
except Exception:
pass
try:
with open(STOCK_SECTOR_MAP_PATH) as f:
data = json.load(f)
# stock_sector_map.json 直接是 code→[sectors] 格式
# 过滤掉 _note, _created_at 等元数据键
code_to_sectors = {}
for key, value in data.items():
if key.startswith("_"):
@@ -94,13 +107,37 @@ def load_stock_sector_map():
def load_market_context():
"""读取市场上下文(market.json),返回 (sector_perf, breadth, sentiment, mood)
sector_perf: {sector_name: sector_data_dict} — 所有行业板块数据
breadth: up_ratio 值(如27.8
sentiment: market.json 的 moodbearish/neutral/bullish
top_gainers/losers: 涨幅/跌幅前5
"""
"""读取市场上下文,优先 SQLite,回退 market.json"""
# 优先从 SQLite 读取
try:
from mofin_db import get_conn, query_latest_market
conn = get_conn()
market = query_latest_market(conn)
conn.close()
if market and market.get("sectors"):
sector_perf = {}
for s in market["sectors"]:
name = s.get("name", "")
if name:
sector_perf[name] = {
"change": s.get("change_pct", 0),
"up_count": s.get("up_count", 0),
"down_count": s.get("down_count", 0),
"net_inflow": s.get("net_inflow", 0),
"lead_stock": s.get("lead_stock", ""),
"lead_stock_change": s.get("lead_stock_change", 0),
}
return {
"sector_perf": sector_perf,
"breadth": market.get("up_ratio", 50),
"mood": market.get("mood", "neutral"),
"top_gainers": {g["name"]: g["change_pct"] for g in market.get("top_gainers", [])},
"top_losers": {g["name"]: g["change_pct"] for g in market.get("top_losers", [])},
"total_sectors": len(market["sectors"]),
"market_timestamp": market.get("timestamp", ""),
}
except Exception:
pass
try:
with open(MARKET_CONTEXT_PATH) as f:
market = json.load(f)
@@ -117,7 +154,6 @@ def load_market_context():
"lead_stock": s.get("lead_stock", ""),
"lead_stock_change": s.get("lead_stock_change", 0),
}
# 涨幅/跌幅前5
top_gainers = {s.get("name", ""): s.get("change", 0)
for s in market.get("top_gainers", [])}
top_losers = {s.get("name", ""): s.get("change", 0)
@@ -896,9 +932,18 @@ def check_sector_alerts(market_ctx, stock_sector_map, holdings, wl):
def regenerate_all(stdout=True):
"""全量重评所有持仓+自选策略"""
pf = safe_json_load(PORTFOLIO_PATH, {})
wl_path = WATCHLIST_PATH
wl = safe_json_load(wl_path, {})
# 优先从 SQLite 读取
try:
from mofin_db import get_conn, query_holdings, query_watchlist
conn = get_conn()
holdings = query_holdings(conn)
wl_stocks = query_watchlist(conn)
conn.close()
pf = {"holdings": holdings}
wl = {"stocks": wl_stocks}
except Exception:
pf = safe_json_load(PORTFOLIO_PATH, {})
wl = safe_json_load(WATCHLIST_PATH, {})
all_stocks = {}
for item in pf.get("holdings", []):