feat: data-layering 第二批消费层读DB(divergence/staleness/accumulation/collect_eval/strategy_review/mo_provider/multi_timeframe/chip_factors)
This commit is contained in:
@@ -14,7 +14,7 @@ divergence_detector.py — 跨市场背离监测器(no_agent)
|
||||
- 状态文件 macro_divergence_state.json
|
||||
- no_agent: 有信号才出声
|
||||
"""
|
||||
import sys, json, re, datetime, os, requests
|
||||
import sys, json, datetime, os
|
||||
from pathlib import Path
|
||||
|
||||
# ── 消息通道统一路由(broadcast/xmpp by delivery) ──
|
||||
@@ -47,69 +47,45 @@ DIVERGENCE_MODERATE = 3.0 # >3% → moderate信号
|
||||
STREAK_DAYS = 3 # 连涨/连跌3天 → 信号
|
||||
|
||||
def fetch_indices():
|
||||
"""获取所有指数实时数据(直接调用新浪API,mo_data不支持指数代码)"""
|
||||
import requests
|
||||
sina_map = {
|
||||
"sh000001": "s_sh000001",
|
||||
"sz399001": "s_sz399001",
|
||||
"sz399006": "s_sz399006",
|
||||
"sh000688": "s_sh000688",
|
||||
"sh000016": "s_sh000016",
|
||||
"sh000300": "s_sh000300",
|
||||
"hkHSI": "rt_hkHSI",
|
||||
"hkHSCEI": "rt_hkHSCEI",
|
||||
"""获取所有指数数据(读 DB stock_daily 收盘价,2026-08-26 分层铁律:消费层不直连新浪API)"""
|
||||
import sqlite3
|
||||
# 指数代码 → 中文名(stock_daily 表已有 sh000001/sz399001/sz399006/sh000688)
|
||||
index_map = {
|
||||
"sh000001": "上证指数",
|
||||
"sz399001": "深证成指",
|
||||
"sz399006": "创业板指",
|
||||
"sh000688": "科创50",
|
||||
"sh000016": "上证50",
|
||||
"sh000300": "沪深300",
|
||||
}
|
||||
url = "http://hq.sinajs.cn/list=" + ",".join(sina_map.values())
|
||||
headers = {"Referer": "https://finance.sina.com.cn"}
|
||||
indices = {}
|
||||
try:
|
||||
r = requests.get(url, headers=headers, timeout=10)
|
||||
r.encoding = "gbk"
|
||||
conn = sqlite3.connect(str(DB_PATH), timeout=5)
|
||||
for sym, cname in index_map.items():
|
||||
rows = conn.execute(
|
||||
"SELECT date, close, high, low FROM stock_daily "
|
||||
"WHERE code=? ORDER BY date DESC LIMIT 2", (sym,)
|
||||
).fetchall()
|
||||
if not rows:
|
||||
continue
|
||||
latest = rows[0]
|
||||
prev_close = rows[1][1] if len(rows) > 1 else latest[1]
|
||||
price = latest[1] or 0
|
||||
change_pct = (price - prev_close) / prev_close * 100 if prev_close else 0
|
||||
indices[sym] = {
|
||||
"name": cname,
|
||||
"price": price,
|
||||
"close": price,
|
||||
"change_pct": round(change_pct, 2),
|
||||
"high": latest[2] or 0,
|
||||
"low": latest[3] or 0,
|
||||
"timestamp": latest[0],
|
||||
}
|
||||
conn.close()
|
||||
except Exception as e:
|
||||
print(f"[DIVERGE] 采集失败: {e}", file=sys.stderr)
|
||||
return {}
|
||||
|
||||
indices = {}
|
||||
for line in r.text.strip().split("\n"):
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
# Parse: var hq_str_XXXX="fields,...";
|
||||
try:
|
||||
var_name = line.split('"')[0].rsplit("_", 1)[-1].rstrip("=")
|
||||
fields = line.split('"')[1].split(",")
|
||||
except (IndexError, ValueError):
|
||||
continue
|
||||
|
||||
# Find the symbol
|
||||
sym = None
|
||||
for s, sn in sina_map.items():
|
||||
if sn.endswith(var_name):
|
||||
sym = s
|
||||
break
|
||||
if not sym:
|
||||
continue
|
||||
|
||||
if sym.startswith("hk"):
|
||||
# HK: name,price,open,high,low,prev_close,change,change_pct,...
|
||||
name = fields[1]
|
||||
price = float(fields[2]) if fields[2] else 0
|
||||
change_pct = float(fields[8]) if len(fields) > 8 and fields[8] else 0
|
||||
else:
|
||||
# A-share: name,price,change,change_pct,...
|
||||
name = fields[0]
|
||||
price = float(fields[1]) if fields[1] else 0
|
||||
change_pct = float(fields[3]) if len(fields) > 3 and fields[3] else 0
|
||||
|
||||
indices[sym] = {
|
||||
"name": name,
|
||||
"price": price,
|
||||
"close": 0,
|
||||
"change_pct": change_pct,
|
||||
"high": 0,
|
||||
"low": 0,
|
||||
"timestamp": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
}
|
||||
|
||||
# 港股指数 hkHSI/hkHSCEI 不在 stock_daily,读不到则不返回(中性值,由调用方跳过低检测)
|
||||
return indices
|
||||
|
||||
def load_history():
|
||||
|
||||
Reference in New Issue
Block a user