feat: 推送流程重构—进操作区间→重评→(可操作→推荐|不可操作→说明)
This commit is contained in:
+70
-16
@@ -14,10 +14,7 @@ no_agent模式:有推送→输出;无→静默
|
||||
搭配 cron: no_agent=True, 交易日每30分跑一次
|
||||
"""
|
||||
import subprocess
|
||||
import sys
|
||||
import re
|
||||
import json
|
||||
import os
|
||||
import sys, re, json, os, time
|
||||
import threading
|
||||
import time
|
||||
from datetime import datetime, time
|
||||
@@ -149,6 +146,37 @@ COOLDOWN_PATH = "/home/hmo/web-dashboard/data/push_cooldown.json"
|
||||
|
||||
NON_BUY_SIGNALS = ["观望", "弱势持有", "深套持有"]
|
||||
|
||||
# 重评冷却:4小时内不重复重评同一股票
|
||||
# Dad确认流程: 进区间→重评→(可操作→推荐|不可操作→说明)→冷却期内不再重评+不推重复
|
||||
REASSESS_COOLDOWN_HOURS = 4
|
||||
|
||||
|
||||
def get_last_reassess_time(code: str):
|
||||
"""从holding_strategies查最近重评时间"""
|
||||
try:
|
||||
db = get_conn()
|
||||
row = db.execute(
|
||||
"SELECT updated_at FROM holding_strategies WHERE code=? AND status IN ('active','updated') ORDER BY updated_at DESC LIMIT 1",
|
||||
(code,)
|
||||
).fetchone()
|
||||
db.close()
|
||||
if row and row[0]:
|
||||
return datetime.strptime(row[0][:19], '%Y-%m-%d %H:%M:%S')
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def is_due_for_reassess(code: str, hours=None) -> bool:
|
||||
"""检查股票是否到重评时间:无历史记录或上次重评超过hours小时"""
|
||||
if hours is None:
|
||||
hours = REASSESS_COOLDOWN_HOURS
|
||||
last = get_last_reassess_time(code)
|
||||
if last is None:
|
||||
return True # 从未重评过→需要
|
||||
elapsed = datetime.now() - last
|
||||
return elapsed.total_seconds() > hours * 3600
|
||||
|
||||
|
||||
def load_macro_line():
|
||||
"""加载大盘和市场的简要描述"""
|
||||
@@ -422,11 +450,12 @@ def main():
|
||||
if macro_line:
|
||||
lines.append(f"【市场背景】{macro_line}")
|
||||
|
||||
# [关键修复: 2026-06-25] 所有预推票先重评,再出报告
|
||||
# 不只是 stale 的重评,所有在买入区的自选都先刷新策略,确保推荐不滞后
|
||||
to_reassess = list(set(s[1] for s in stocks) | set(s[1] for s in stale_list))
|
||||
if to_reassess:
|
||||
trigger_regen_sync(to_reassess)
|
||||
# [关键修复: 2026-07-09] Dad确认流程:进区间→重评→(可操作→推荐|不可操作→说明)
|
||||
# 冷却期内不再重复重评同一股票:查DB holding_strategies.updated_at
|
||||
all_codes_in_zone = list(set(s[1] for s in stocks) | set(s[1] for s in stale_list))
|
||||
needs_reassess = [c for c in all_codes_in_zone if is_due_for_reassess(c)]
|
||||
if needs_reassess:
|
||||
trigger_regen_sync(needs_reassess)
|
||||
# 重评完成,re-read 最新策略(从DB)
|
||||
code_data = {}
|
||||
try:
|
||||
@@ -449,18 +478,33 @@ def main():
|
||||
|
||||
# 重建 stocks 列表,用新数据判断(不再用旧 is_stale 标记,因为已全部重评)
|
||||
stocks = []
|
||||
zone_notes = [] # 在操作区间但不可操作→发说明
|
||||
for (name, code, price, buy_low, buy_high, cur, is_stale) in all_candidates:
|
||||
# 重评后重新检查 actionability(用新 timing_signal)
|
||||
sig = code_data.get(code, {}).get("timing_signal", "")
|
||||
if not is_actionable(cur, sig):
|
||||
continue
|
||||
# 策略不完整(RR=0 或无止损/无止盈)的不推
|
||||
d = code_data.get(code, {})
|
||||
rr = d.get("rr_ratio", 0) or 0
|
||||
sl = d.get("stop_loss", 0) or 0
|
||||
tp = d.get("take_profit", 0) or 0
|
||||
if rr <= 0 or sl <= 0 or tp <= 0:
|
||||
|
||||
# 判断重评后的可操作性
|
||||
reason = ""
|
||||
if not is_actionable(cur, sig):
|
||||
reason = f"信号'{sig}'非可操作方向"
|
||||
elif rr <= 0 or sl <= 0 or tp <= 0:
|
||||
reason = f"策略不完整(RR={rr} 损={sl} 盈={tp})"
|
||||
elif any(kw in sig for kw in ["等企稳", "信号不充分"]):
|
||||
reason = f"信号'{sig}',暂不建议操作"
|
||||
|
||||
if reason:
|
||||
# 冷却检查:同股同原因4小时内不发(匹配重评冷却)
|
||||
ck = f"zone_note_{code}"
|
||||
now_ts = datetime.now().timestamp()
|
||||
last = cooldown.get(ck, 0)
|
||||
if now_ts - last > REASSESS_COOLDOWN_HOURS * 3600:
|
||||
zone_notes.append((name, code, price, buy_low, buy_high, reason, sig))
|
||||
cooldown[ck] = now_ts
|
||||
continue
|
||||
|
||||
lot = lot_cost(code, price)
|
||||
ratio = lot / cash if cash > 0 else 999
|
||||
stocks.append((name, code, price, buy_low, buy_high, lot, ratio))
|
||||
@@ -535,8 +579,8 @@ def main():
|
||||
|
||||
actionable.append(s)
|
||||
|
||||
if not actionable:
|
||||
return 0 # 无操作信号 → 静默,不推
|
||||
if not actionable and not zone_notes:
|
||||
return 0 # 无推荐也无区间说明 → 静默
|
||||
|
||||
# 加载基本面缓存(PE等)
|
||||
fund_cache = {}
|
||||
@@ -953,6 +997,16 @@ def main():
|
||||
lines.append("【⏳ 提前准备(T+2港股提前出清)】")
|
||||
lines.extend(t2_lines)
|
||||
|
||||
# 操作区间内但重评后不可操作的→发说明
|
||||
if zone_notes:
|
||||
lines.append("")
|
||||
lines.append("【📋 操作区间提醒】进入操作区间,但重评后不构成推荐操作:")
|
||||
for name, code, price, buy_low, buy_high, reason, sig in zone_notes:
|
||||
lines.append(
|
||||
f" {name}({code}) 价{price:.2f} 区间{buy_low}~{buy_high} "
|
||||
f"→ 重评结果: {reason}"
|
||||
)
|
||||
|
||||
lines.insert(0, f"【知微】自选买入提醒 {now} | 总资产{total_assets:,.0f}元")
|
||||
out = "\n".join(lines)
|
||||
print(out)
|
||||
|
||||
Reference in New Issue
Block a user