stale_push_wlin: 30分钟同股同操作冷却

push_cooldown.json 记录每只股票每种操作的最后推送时间。
每次推送前检查 (code, action_type) 是否在30分钟内推过。
是 → 跳过该股(不出现在推送中)
全部跳过 → 整条消息静默不推

冷却键: {code}_{action_type}(如 300308_buy、688639_buy)
不同操作不受限:同一只股 买入→止损 隔10分钟也能推
不同股票不受限:华恒的buy不影响中际的buy

同步修复:港股每手股数香港股数(之前patch到旧文件没生效)
This commit is contained in:
知微
2026-06-24 11:01:53 +08:00
parent e7105543da
commit 3ec5460207
4 changed files with 203 additions and 144 deletions
+76 -17
View File
@@ -17,6 +17,7 @@ import re
import json
import os
import threading
import time
from datetime import datetime
try:
from urllib.request import Request, urlopen
@@ -33,6 +34,7 @@ REGEN_SCRIPT = "/home/hmo/.hermes/profiles/position-analyst/scripts/per_stock_re
REGEN_LOCK = "/tmp/.stale_push_wlin_regen.lock"
MACRO_CTX = "/home/hmo/web-dashboard/data/macro_context.json"
MARKET_JSON = "/home/hmo/web-dashboard/data/market.json"
COOLDOWN_PATH = "/home/hmo/web-dashboard/data/push_cooldown.json"
NON_BUY_SIGNALS = ["观望", "弱势持有", "深套持有"]
@@ -156,6 +158,29 @@ def push_to_xmpp(text):
print(f"[XMPP推送失败] {e}", file=sys.stderr)
def load_cooldown():
try:
with open(COOLDOWN_PATH) as f:
return json.load(f)
except Exception:
return {}
def save_cooldown(cd):
try:
with open(COOLDOWN_PATH, "w") as f:
json.dump(cd, f, indent=2)
except Exception:
pass
def in_cooldown(code, action_type, cooldown_dict, minutes=30):
key = f"{code}_{action_type}"
last = cooldown_dict.get(key, 0)
elapsed = time.time() - last
return elapsed < minutes * 60, elapsed, key
def main():
r = subprocess.run(
["python3", DETECTOR], capture_output=True, text=True, timeout=60
@@ -178,6 +203,10 @@ def main():
report = {"flagged": []}
code_cur = {i["code"]: i.get("current", "") for i in report.get("flagged", [])}
# 加载冷却状态
cooldown = load_cooldown()
now_ts = time.time()
# 读 decisions.json 获取完整策略数据
code_data = {}
try:
@@ -358,7 +387,13 @@ def main():
if lots == 0:
details = f"预算不足1手({budget:,.0f}/{lot_cost:,.0f}元)"
else:
shares = lots * (200 if code.startswith("688") else 100)
if len(str(code)) == 5:
hk_lot = hk_lot_size(code)
shares = lots * hk_lot
elif code.startswith("688"):
shares = lots * 200
else:
shares = lots * 100
details = f"{lots}手({shares}股,{lot_cost_total:,.0f}元)"
return theo_pct, pct_actual, details, lots, lot_cost_total
@@ -426,6 +461,26 @@ def main():
)
pfx = "" if len(code) == 6 else "HK$"
# 取分支动作类型
branch_action = "hold"
branch_rationale = ""
if st and scenario_id:
try:
results = st.evaluate_branches(code, scenario_id, price, d.get("shares", 0), d.get("cost", 0))
applicable = [r for r in results if r.get("applicable")]
if applicable:
best = min(applicable, key=lambda r: r.get("priority", 999))
branch_action = best.get("action_type", "hold")
branch_rationale = best.get("rationale", "")
except Exception:
pass
# 冷却检查:相同股+相同操作30分钟内不发
cooled, elapsed, cd_key = in_cooldown(code, branch_action, cooldown)
if cooled:
continue
action_tag = "⚠️" if lots == 0 else "🛒"
lines.append(
f" {action_tag} {name}({code}) {pfx}{price:.2f} 买区{buy_low}~{buy_high} | "
@@ -435,26 +490,30 @@ def main():
f" 仓位:理论{theo_pct}%×总资产 | 建议{actual_pct}%{details}"
)
# 分支评估
# 分支描述
branch_line = ""
if st and scenario_id:
try:
results = st.evaluate_branches(code, scenario_id, price, d.get("shares", 0), d.get("cost", 0))
applicable = [r for r in results if r.get("applicable")]
if applicable:
# 取优先级最高的适用分支
best = min(applicable, key=lambda r: r.get("priority", 999))
action = best.get("action_type", "hold")
rationale = best.get("rationale", "")
branch_line = f"{scenario_label}{action}{rationale}"
else:
branch_line = f"{scenario_label}→持有】无匹配分支"
except Exception:
branch_line = ""
if branch_action != "hold":
branch_line = f"{scenario_label}{branch_action}{branch_rationale}"
if branch_line:
# 追加到上一个元素
lines[-1] += f"\n{branch_line}"
# 记录推送时间(冷却计时用)
cooldown[cd_key] = now_ts
save_cooldown(cooldown)
# 修正可操作数量(剔除冷却跳过后的实际数量)
actual_n = len(lines) - (1 if macro_line else 0) - 1 # 减去市场背景 + 操作建议标题
if actual_n != n:
# 更新操作建议行
for i, ln in enumerate(lines):
if "【💡 操作建议】" in ln:
lines[i] = f"【💡 操作建议】(当前{actual_n}只自选可操作 | 总资产{total_assets:,.0f}元 现金{available_cash:,.0f}元)"
break
if actual_n <= 0:
return 0 # 全部冷却中 → 静默,不推
lines.insert(0, f"【知微】自选买入提醒 {now} | 总资产{total_assets:,.0f}")
out = "\n".join(lines)
print(out)