From 23dd333ae4943f713a816dbe45ee1c8d9d600dff Mon Sep 17 00:00:00 2001 From: aaron <> Date: Thu, 23 Apr 2026 17:36:07 +0800 Subject: [PATCH] 1 --- .../__pycache__/intraday.cpython-313.pyc | Bin 16945 -> 17020 bytes .../__pycache__/market_temp.cpython-313.pyc | Bin 5129 -> 5882 bytes backend/app/analysis/intraday.py | 10 ++-- backend/app/analysis/market_temp.py | 17 ++++++ .../__pycache__/recommender.cpython-313.pyc | Bin 42832 -> 43552 bytes backend/app/engine/recommender.py | 52 ++++++++++++------ backend/astock.db | Bin 2797568 -> 2797568 bytes 7 files changed, 57 insertions(+), 22 deletions(-) diff --git a/backend/app/analysis/__pycache__/intraday.cpython-313.pyc b/backend/app/analysis/__pycache__/intraday.cpython-313.pyc index 666c13d02a0cf0312ca90c8a2e74038123d0d63c..5459eee6db425ff5464553766a2ce8b8addf693a 100644 GIT binary patch delta 1727 zcmb7EUu;uV96qP*?QVP9u5f$X+x_cp8FVch-JpeS-53Q4pkZrwy#ZH~3EP0ity^x} zpz#4TLjm1Dj*18m1`{<=0;Dk#BoP8b5D4vFoCQ%x0FA^zG|Gb#f2V`FC@-GoeErV% z`*Xf?Zf}nc<3ES7P@XTxp^P$Gg$)!n?Y~|4kyz(7{G8%-2 zuEqT{nU2m{BDrkjMv1esROV$HOkNgbJA539Sr%pIJY&+Tl#=&Z8>wYEY$c7XTbL1F zFjRdWLNWAgnG45 zE%ZR`Y8OIr$eSFB5W)|crTGNp(TsY?m3FsiT+6bLu#RWN#fqamr@Jv>ZmRnar7rRw|m}_3q+N7rM!xa87D8FK9-mUNMzi5LXAw0Ri%xk%1b$-Crv zO@?a3q7~ZqYeg*EWA4BcBx)A;FF??=?*hc_H;Nb|FPH=PLH1+w0X>$;a$9Gx;86S4 z^`{d9eck-IB-z>$W$7#tS^HQ;lF}N87lg?VdqfWxN~3sH|bTWuiQoW5uhiCA&=&FwT(u&Mw?ees?a|QiAv6 zjwaoKh^4axpPCz4)X|1cdvHgK?)0okEiA^Y)iDPe3*u;i9SdnGZ=4$qGiPVv=zR8U zb)*p%=cEOI=Xo3rvFB}(Ff1T(N)=9jm$8Kbq@ zoI#9wwK=yQ0y!^3m0B(30Spzn`4~FoLPcyWDq(%hX2@e4(^>Kw6Xg~IWab^lPMtRI z7GrJNd=u@KZ(~3i*U@eTZqs18JJr{d9#m63ldo{rTkbaD-DH(_753s%4*)i+sNUXr#TfOY88bM zV)6O$F4E-d(7!>2Q8McD;H~7SkB1G$e2q}@z$aUGK(N59?^Xvl7w~%ZO|s4(W4;1$ zXZCadCLKPRl>$qdh15-P_kv;Fz)*i8o$2jUcTsT$g-i4^|Geid{dF5py(-iD-lX!2h2U3hNU22fHgthb_L_)F7q zle({%tiDG)EnPFer1wTlt9r4ugZTx7=Io``r!@F5aW0ADn0nbiIk5v`Vi{?WRyXGp$nJra+Ask5*t?1_paG{e6qozsU2EHR1-0^|^+ncTth7 RSo>R%-0dWVh^(V!e*=|Toj?Ep delta 1666 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 MarketTem # ── 用东方财富 clist API 统计涨停跌停(比腾讯涨停价字段更可靠) ── try: - limit_up_count = 0 - limit_down_count = 0 + realtime_limit_up_count = 0 + realtime_limit_down_count = 0 for fs, threshold in [ ("m:0+t:6,m:0+t:80,m:0+t:81+s:2048", 9.9), # 主板 10% @@ -84,7 +84,7 @@ async def intraday_market_temperature(prev_temp: MarketTemperature) -> MarketTem if pct == "-" or pct is None: continue if float(pct) >= threshold: - limit_up_count += 1 + realtime_limit_up_count += 1 # 跌停:按涨幅升序取 top 200 params_down = { @@ -102,8 +102,10 @@ async def intraday_market_temperature(prev_temp: MarketTemperature) -> MarketTem if pct == "-" or pct is None: continue if float(pct) <= neg_threshold: - limit_down_count += 1 + realtime_limit_down_count += 1 + limit_up_count = realtime_limit_up_count + limit_down_count = realtime_limit_down_count logger.info(f"东方财富盘中涨跌停: 涨停={limit_up_count} 跌停={limit_down_count}") except Exception as e: logger.warning(f"东方财富涨跌停统计失败,使用基线数据: {e}") diff --git a/backend/app/analysis/market_temp.py b/backend/app/analysis/market_temp.py index 55df9290..e6ee7f5a 100644 --- a/backend/app/analysis/market_temp.py +++ b/backend/app/analysis/market_temp.py @@ -26,6 +26,23 @@ def calculate_market_temperature(trade_date: str = None) -> MarketTemperature: # 1. 涨跌家数 daily_df = tushare_client.get_daily_all(trade_date) if daily_df.empty: + original_trade_date = trade_date + trade_dates = tushare_client.get_trade_dates() + fallback_dates = [d for d in trade_dates if d < trade_date][-5:] + for fallback_date in reversed(fallback_dates): + fallback_df = tushare_client.get_daily_all(fallback_date) + if not fallback_df.empty: + daily_df = fallback_df + trade_date = fallback_date + logger.warning( + "市场温度 %s 日线数据为空,回退到最近有效交易日 %s", + original_trade_date, + fallback_date, + ) + break + + if daily_df.empty: + logger.warning("市场温度 %s 无有效日线数据,返回占位温度", trade_date) return MarketTemperature(trade_date=trade_date, temperature=50) up_count = len(daily_df[daily_df["pct_chg"] > 0]) diff --git a/backend/app/engine/__pycache__/recommender.cpython-313.pyc b/backend/app/engine/__pycache__/recommender.cpython-313.pyc index e27dd600a8cad057a41f1ab4a4d5afd68b7c5f60..c6b9611d68d5c1fa44bca376230b9811299261cb 100644 GIT binary patch delta 7490 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 R_e9lX`O^OufaV7j{|8vR-nak& delta 6842 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 bool: + if not market_temp: + return False + return (market_temp.up_count or 0) + (market_temp.down_count or 0) > 0 + + async def refresh_recommendations(trade_date: str = None, scan_session: str = "manual") -> dict: """刷新推荐列表(带扫描锁防止并发)""" global _scan_running @@ -633,23 +639,32 @@ async def _save_to_db(result: dict): # 保存市场温度 mt = result.get("market_temp") if mt: - # 使用 INSERT OR REPLACE 确保重复扫描能更新数据 - stmt = text( - "INSERT OR REPLACE INTO market_temperature " - "(trade_date, up_count, down_count, limit_up_count, limit_down_count, " - "max_streak, broken_rate, temperature) " - "VALUES (:td, :up, :down, :lu, :ld, :ms, :br, :temp)" - ) - await db.execute(stmt, { - "td": mt.trade_date, - "up": mt.up_count, - "down": mt.down_count, - "lu": mt.limit_up_count, - "ld": mt.limit_down_count, - "ms": mt.max_streak, - "br": mt.broken_rate, - "temp": mt.temperature, - }) + if _has_valid_market_breadth(mt): + # 使用 INSERT OR REPLACE 确保重复扫描能更新数据 + stmt = text( + "INSERT OR REPLACE INTO market_temperature " + "(trade_date, up_count, down_count, limit_up_count, limit_down_count, " + "max_streak, broken_rate, temperature) " + "VALUES (:td, :up, :down, :lu, :ld, :ms, :br, :temp)" + ) + await db.execute(stmt, { + "td": mt.trade_date, + "up": mt.up_count, + "down": mt.down_count, + "lu": mt.limit_up_count, + "ld": mt.limit_down_count, + "ms": mt.max_streak, + "br": mt.broken_rate, + "temp": mt.temperature, + }) + else: + logger.warning( + "跳过无效市场温度快照: trade_date=%s temperature=%s up=%s down=%s", + mt.trade_date, + mt.temperature, + mt.up_count, + mt.down_count, + ) # 保存板块热度(先清除同一 trade_date 的旧数据,再批量插入) trade_date_val = mt.trade_date if mt else "" @@ -769,7 +784,8 @@ async def _load_today_from_db() -> dict: result = await db.execute( text( "SELECT * FROM market_temperature " - "ORDER BY REPLACE(trade_date, '-', '') DESC, id DESC LIMIT 1" + "ORDER BY CASE WHEN COALESCE(up_count, 0) + COALESCE(down_count, 0) > 0 THEN 0 ELSE 1 END, " + "REPLACE(trade_date, '-', '') DESC, id DESC LIMIT 1" ) ) mt_row = result.fetchone() diff --git a/backend/astock.db b/backend/astock.db index 6c8e9bcaa5691318c4a9886a0ab332d86937d0a5..e72a35bc5760ffb1ad985a7993f7639314ebed93 100644 GIT binary patch delta 197 zcmWN@w+RAK002%qtk|