feat: predictive_oversold_scanner完善——真实全市场PE/市值分位(get_market_percentile)+行业20日动量(fetch_sector_momentum),替代简化值

This commit is contained in:
hmo
2026-08-11 15:20:09 +08:00
parent c582853873
commit d61bd7024e
@@ -228,12 +228,14 @@ def main():
fund = fetch_fundamentals(code)
if not fund:
continue
# 简化分位(部署时可从全市场分位表读取
mcap_q = 0.1 if fund.get("mcap_total") and fund["mcap_total"] < 50 else 0.5
pe_q = 0.1 if fund.get("pe") and fund["pe"] < 20 else 0.5
# 简化 news3/sec_ret20(部署时可从 stock_news/sector 数据读取)
news3 = 1 if fetch_news_count(code) > 0 else 0
sec_ret20 = -5 # 简化,部署时用真实行业动量
# 2026-08-11:真实分位(全市场 PE/市值分位,替代简化值
mcap_q = get_market_percentile(code, "mcap_total")
pe_q = get_market_percentile(code, "pe")
if mcap_q is None or pe_q is None:
continue
# 2026-08-11:真实新闻数(3日)+ 行业20日动量
news3 = fetch_news_count(code)
sec_ret20 = fetch_sector_momentum(code)
ok_s, msg_s = check_stock(code, code, mcap_q, pe_q, news3, sec_ret20, klines)
if ok_s:
@@ -250,6 +252,57 @@ def main():
print(f" ✅ 完成: 新增{hits}只 p_oversold 候选", flush=True)
def get_market_percentile(code, field):
"""计算个股在全市场的分位(0-1,越小越优)。
field: mcap_total 或 pe。分位 = (比它小的数量 / 总数)。
返回 None 表示数据不可用。"""
try:
conn = sqlite3.connect(str(DB_PATH), timeout=5)
# 全市场分布
total = conn.execute(f"SELECT COUNT(*) FROM stock_fundamentals WHERE {field} > 0").fetchone()[0]
if not total:
conn.close()
return None
mine = conn.execute(f"SELECT {field} FROM stock_fundamentals WHERE code=?", (code,)).fetchone()
if not mine or not mine[0] or mine[0] <= 0:
conn.close()
return None
val = mine[0]
# 分位:比我小的占比
cnt = conn.execute(f"SELECT COUNT(*) FROM stock_fundamentals WHERE {field} > 0 AND {field} < ?", (val,)).fetchone()[0]
conn.close()
return cnt / total
except Exception:
return None
def fetch_sector_momentum(code):
"""计算行业20日动量(%)。从 stock_sectors 拿行业,再算行业指数20日涨跌。
返回 None 表示无行业数据。"""
try:
conn = sqlite3.connect(str(DB_PATH), timeout=5)
sector = conn.execute(
"SELECT sector_name FROM stock_sectors WHERE code=? LIMIT 1", (code,)).fetchone()
if not sector or not sector[0]:
conn.close()
return None
sector_name = sector[0]
# 行业20日动量:查 sector_index_daily 该行业20日前 vs 最新
rows = conn.execute(
"SELECT close FROM sector_index_daily WHERE sector=? ORDER BY date DESC LIMIT 21",
(sector_name,)).fetchall()
conn.close()
if len(rows) < 20:
return None
latest = rows[0][0]
past = rows[19][0]
if past <= 0:
return None
return round((latest - past) / past * 100, 2)
except Exception:
return None
def fetch_news_count(code):
"""简化:查 stock_news 表近3日新闻数(部署时可完善)"""
try: