From 39ff4d95f7a687939dd5571b7f8efeaf3028c764 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=9F=A5=E5=BE=AE?= Date: Wed, 24 Jun 2026 22:34:08 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20macro=5Fcontext/market=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=85=A8=E9=83=A8DB=E4=BC=98=E5=85=88=EF=BC=8CJSON?= =?UTF-8?q?=E5=9B=9E=E9=80=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 建 macro_context_log 表,macro_context_collector.py 双写 - strategy_lifecycle.py load_macro_context() 优先DB - strategy_tree.py detect_scenario() 优先DB - stale_push_wlin.py load_macro_line() 优先DB - xiaoguo_signal_consumer.py 大盘判断优先DB - stock_profile.py load_macro() 优先DB - system_audit.py 管道审计改查DB market_snapshots - JSON保留作fallback,确保过渡期不中断 --- __pycache__/stock_profile.cpython-312.pyc | Bin 18470 -> 19373 bytes .../strategy_lifecycle.cpython-312.pyc | Bin 70658 -> 72153 bytes scripts/stale_push_wlin.py | 28 ++++++++++-- scripts/xiaoguo_signal_consumer.py | 34 +++++++++++--- stock_profile.py | 16 ++++++- strategy_lifecycle.py | 43 ++++++++++++------ strategy_tree.py | 31 +++++++++---- system_audit.py | 19 ++++---- 8 files changed, 132 insertions(+), 39 deletions(-) diff --git a/__pycache__/stock_profile.cpython-312.pyc b/__pycache__/stock_profile.cpython-312.pyc index e6114d62e56cc197f527161de68bc2714631d42b..40bc964c38914489c977f8ae9b072cbc4f27bae4 100644 GIT binary patch delta 1808 zcmah}ZA?>F7(VCTTlzr@DlIy6aIu?E6o?|pq)-NJ8Aee9WQMt=>3XlWUE3??-i{B; zsw{?~Y_OdLCDWllCLewbZKlSlbAc_{_7k({oj+V6i&?fj#s`0LZ%bt3CL<<80Pp<08J?FNMW&e^@GSk{SY$#8u;REwo`Z zhljoR@0=Y_#@o#`#V!VbLqHD%C@>+=17aY^(o#<^g~M0PE7k|~Ps`L(Qk^Li*ps9f zT_@pV+XG_|Z>zBAxwL`{SDEqc^}386CI*9x@!5>LI0P{UwJJG}a{vgM(`??x7y&*< z+)gMn5D1!dy=P`+JTogk-{lD-&9n>Bbm?Y-{s`Mb&PorIrRgMPS(>GzDnSpsV643K z^LZO%)3H8AV73SZ(kBN&3$OwR+Q2|XC))-(pcSkIGVCC`ZX-GbT})b6R-{WROVTWu zkBcp<`o-?q{r{@oyHZQ?4f4=&iRq%fK|qc0n+mZE;qz z(=X^LFJqkR*y&1*)1jTNVyWVwI0Kiv3fP%^ND%Eyg0eRc6hsFw4b;*%Pzp56S6%Ul1bmKUI)+P^VJRT z3jVixrTHtGOZT~}BDH2~(I>b)D+f2!L@cN1!_yQN&i7qSMP^ETGOyNQT}(Hn>U~mp zw7{hRiHfdfvcOxuIosl># zXOEP)LVL3`CguX|gU~mNz692XE`HR|4}u_=^{Ml$AkT&0{h$By!gJ2Ou}5SgLQ+5L z^%@0RO<5%^+sEFfj_61}d`ZfP&*}E7ER#eg%@SepY!Cfc zWxPPPpx#sgi-txeakB@qjG2T$h0)n*-4je_b*tg=wS|S%>A|&Ep_TdY%6$KihrOq* z$yAR`HK**3hI;;ty~#;$F@xSlbRxPC-3+^j(EJ_3B~X#}vRFID>lwNfn;fS!EbG&T z4=?F6Hx*zur$esEs-kyTn1FZqIdB zyCkPb0U8tnLF$2MrJH=4t=daS81~sp$Rs#yC&&zhZ5GokHo(JPDNU@UDtaG2*t*ll zFffkTJ>WTesWxH$8+h2BlZK~~Qv}i9CQ815hfcvqdmV{^soI>@Amt&$T=E%xxL6H?E^8R1cdraQJxF+Bcyu2ic)I;acod>nUuUbL5t$+?kp;F6! z!}IYOhm$)H#S%5o`(3_Pp0_5^ubB58u?!|>3n%XpCFpVHot4!!_9i=DQ`;bOZLWY< z=2}EwEA7D4yNGuPFQOT7nL*~tMLm!jAJOi9&I$kz~b>*G>>!GBHGI QwiN8OX@H+6XE~hk585sO%m4rY diff --git a/__pycache__/strategy_lifecycle.cpython-312.pyc b/__pycache__/strategy_lifecycle.cpython-312.pyc index 6cc3c3656f42c7ba74c710934695a083ae3c1b25..9d8d82df6746b371572a19ffb1da51c53c747c92 100644 GIT binary patch delta 7886 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 diff --git a/scripts/stale_push_wlin.py b/scripts/stale_push_wlin.py index 402b8d1..bb5341c 100644 --- a/scripts/stale_push_wlin.py +++ b/scripts/stale_push_wlin.py @@ -112,8 +112,18 @@ def load_macro_line(): """加载大盘和市场的简要描述""" parts = [] try: - with open(MACRO_CTX) as f: - m = json.load(f).get("structure", {}) + # 优先 DB + import sqlite3 + db = sqlite3.connect("/home/hmo/MoFin/data/mofin.db") + row = db.execute( + "SELECT structure FROM macro_context_log " + "WHERE has_valid_data=1 ORDER BY created_at DESC LIMIT 1" + ).fetchone() + db.close() + if row and row[0]: + m = json.loads(row[0]) + else: + raise ValueError("no db data") overall = m.get("overall", "neutral") desc = m.get("description", "") if "bearish" in overall: @@ -123,7 +133,19 @@ def load_macro_line(): elif desc: parts.append(f"大盘{desc}") except Exception: - pass + try: + with open(MACRO_CTX) as f: + m = json.load(f).get("structure", {}) + overall = m.get("overall", "neutral") + desc = m.get("description", "") + if "bearish" in overall: + parts.append("大盘偏弱") + elif overall == "bullish": + parts.append("大盘偏强") + elif desc: + parts.append(f"大盘{desc}") + except Exception: + pass try: with open(MARKET_JSON) as f: mk = json.load(f) diff --git a/scripts/xiaoguo_signal_consumer.py b/scripts/xiaoguo_signal_consumer.py index d8e35f1..50c1cd5 100644 --- a/scripts/xiaoguo_signal_consumer.py +++ b/scripts/xiaoguo_signal_consumer.py @@ -74,19 +74,41 @@ def quick_assess(quote): score = 0 reasons = [] - # 大盘环境(简化:交易日9:30-15:00且在涨) - # 引用 macro_context.json 中的大盘方向 + # 大盘环境,从DB读(回退JSON) try: - mc = json.loads((DATA / "macro_context.json").read_text()) - sh = mc.get("shanghai", {}).get("change_pct", 0) + import sqlite3 + conn = sqlite3.connect(str(DB_PATH)) + row = conn.execute( + "SELECT indices FROM macro_context_log WHERE has_valid_data=1 ORDER BY created_at DESC LIMIT 1" + ).fetchone() + conn.close() + if row and row[0]: + mc = json.loads(row[0]) + else: + raise ValueError + sh = 0 + for k, v in mc.items(): + if "上证" in k: + sh = v.get("change_pct", 0) + break if sh > 0.5: score += 1 reasons.append(f"大盘+{sh:.1f}%偏强") elif sh < -0.5: score -= 1 reasons.append(f"大盘{sh:.1f}%偏弱") - except: - pass + except Exception: + try: + mc = json.loads((DATA / "macro_context.json").read_text()) + sh = mc.get("shanghai", {}).get("change_pct", 0) + if sh > 0.5: + score += 1 + reasons.append(f"大盘+{sh:.1f}%偏强") + elif sh < -0.5: + score -= 1 + reasons.append(f"大盘{sh:.1f}%偏弱") + except: + pass # 技术面:涨跌幅 chg = quote.get("change_pct", 0) diff --git a/stock_profile.py b/stock_profile.py index c02fdc5..1fb7c04 100644 --- a/stock_profile.py +++ b/stock_profile.py @@ -119,7 +119,21 @@ def load_mtf_cache() -> dict: def load_macro() -> dict: - """加载宏观上下文""" + """加载宏观上下文,优先DB""" + try: + import sqlite3 + conn = sqlite3.connect(os.path.join(DATA_DIR, "mofin.db")) + row = conn.execute( + "SELECT indices, structure, key_sectors FROM macro_context_log " + "WHERE has_valid_data=1 ORDER BY created_at DESC LIMIT 1" + ).fetchone() + conn.close() + if row: + return {"indices": json.loads(row[0] or "{}"), + "structure": json.loads(row[1] or "{}"), + "key_sectors": json.loads(row[2] or "[]")} + except: + pass try: with open(MACRO_PATH) as f: return json.load(f) diff --git a/strategy_lifecycle.py b/strategy_lifecycle.py index 4a0f6bf..616b748 100644 --- a/strategy_lifecycle.py +++ b/strategy_lifecycle.py @@ -286,22 +286,39 @@ def compute_sector_adjustment(code, market_ctx, stock_sector_map): def load_macro_context(): - """读取宏观上下文,返回 (bias, desc),bias 取 0.8/1.0/1.1 分别对应 bearish/neutral/bullish""" + """读取宏观上下文,返回 (bias, desc),优先 DB,回退 JSON""" try: - with open(MACRO_CONTEXT_PATH) as f: - ctx = json.load(f) - overall = ctx.get("structure", {}).get("overall", "neutral") - desc = ctx.get("structure", {}).get("description", "") - if "bearish" in overall: - return 0.8, f"宏观{desc}" - elif overall == "bullish": - return 1.05, f"宏观{desc}" - elif overall == "strong_bullish": - return 1.1, f"宏观{desc}" + import sqlite3 + from pathlib import Path + conn = sqlite3.connect(str(Path(__file__).parent.parent / "data" / "mofin.db")) + row = conn.execute( + "SELECT indices, structure FROM macro_context_log " + "WHERE has_valid_data=1 ORDER BY created_at DESC LIMIT 1" + ).fetchone() + conn.close() + if row: + indices = json.loads(row[0]) if row[0] else {} + structure = json.loads(row[1]) if row[1] else {} + overall = structure.get("overall", "neutral") + desc = structure.get("description", "") else: - return 1.0, f"宏观{desc}" + raise ValueError("no db data") except Exception: - return 1.0, "宏观未加载" + try: + with open(MACRO_CONTEXT_PATH) as f: + ctx = json.load(f) + overall = ctx.get("structure", {}).get("overall", "neutral") + desc = ctx.get("structure", {}).get("description", "") + except Exception: + return 1.0, "宏观未加载" + if "bearish" in overall: + return 0.8, f"宏观{desc}" + elif overall == "bullish": + return 1.05, f"宏观{desc}" + elif overall == "strong_bullish": + return 1.1, f"宏观{desc}" + else: + return 1.0, f"宏观{desc}" def batch_fetch_prices(codes): diff --git a/strategy_tree.py b/strategy_tree.py index c85f43c..985cea0 100644 --- a/strategy_tree.py +++ b/strategy_tree.py @@ -70,15 +70,30 @@ def detect_scenario(): confidence = 0.5 try: - macro = json.load(open(MACRO_PATH)) - market = json.load(open(MARKET_PATH)) + # 优先 DB + import sqlite3 + from pathlib import Path + db = sqlite3.connect(str(Path(__file__).parent / "data" / "mofin.db")) + mrow = db.execute( + "SELECT indices, structure, sector_mood FROM macro_context_log " + "WHERE has_valid_data=1 ORDER BY created_at DESC LIMIT 1" + ).fetchone() + db.close() + if mrow: + structure = json.loads(mrow[1]) if mrow[1] else {} + overall = structure.get("overall", "").lower() + mood = (mrow[2] or "").lower() if len(mrow) > 2 else "" + else: + raise ValueError("no db data") except Exception: - return {"id": scenario_id, "label": "默认-弱势震荡", "confidence": 0.3, "portfolio_action": "观望"} - - mood = market.get("mood", "").lower() - # Try to get trend from macro_context - structure = macro.get("structure", {}) - overall = structure.get("overall", "").lower() + try: + macro = json.load(open(MACRO_PATH)) + market = json.load(open(MARKET_PATH)) + mood = market.get("mood", "").lower() + structure = macro.get("structure", {}) + overall = structure.get("overall", "").lower() + except Exception: + return {"id": "weak_consolidation", "label": "默认-弱势震荡", "confidence": 0.3, "portfolio_action": "观望"} trend_desc = structure.get("description", "").lower() # Check for sharp decline diff --git a/system_audit.py b/system_audit.py index 14cb0fc..8d1b004 100644 --- a/system_audit.py +++ b/system_audit.py @@ -128,16 +128,19 @@ def audit_portfolio(conn): # ── 6. 数据管道审计 ── def audit_pipeline(): - # 检查market.json是否今天更新 + # 检查DB市场数据是否今天更新 try: - mkt = json.loads((DATA_DIR / "market.json").read_text()) - mkt_ts = mkt.get("timestamp", "") - if mkt_ts[:10] == datetime.now().strftime("%Y-%m-%d"): - log_ok("数据管道", f"市场数据今天更新({mkt_ts})") + conn = sqlite3.connect(str(DATA_DIR / "mofin.db")) + row = conn.execute( + "SELECT created_at FROM market_snapshots ORDER BY created_at DESC LIMIT 1" + ).fetchone() + conn.close() + if row and row[0][:10] == datetime.now().strftime("%Y-%m-%d"): + log_ok("数据管道", f"市场数据今天更新({row[0]})") else: - log_issue("数据管道", "HIGH", f"市场数据未更新, 最后{mkt_ts}") - except: - log_issue("数据管道", "HIGH", "market.json缺失") + log_issue("数据管道", "HIGH", f"市场数据未更新(DB), 最后{row[0] if row else '无数据'}") + except Exception as e: + log_issue("数据管道", "HIGH", f"DB检查失败: {e}") # ── 7. 系统服务 ──