This commit is contained in:
aaron 2026-04-23 17:36:07 +08:00
parent c03d5a88e8
commit 23dd333ae4
7 changed files with 57 additions and 22 deletions

View File

@ -61,8 +61,8 @@ async def intraday_market_temperature(prev_temp: MarketTemperature) -> 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}")

View File

@ -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])

View File

@ -21,6 +21,12 @@ _scan_lock = asyncio.Lock()
_scan_running = False
def _has_valid_market_breadth(market_temp: MarketTemperature | None) -> 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()

Binary file not shown.