Files
Hermes-Skills/crypto-t-monitor/references/change-driven-push.md
T
mikeandClaude 4aae222173 feat(crypto-t-monitor): v2.3.0 新币自动挑选池 (PICKS=2, POOL_MAX=6)
- NEW_COIN_PICKS=2: 每次扫描后筛 2 个 (按 24h vol 排序)
- NEW_COIN_POOL_MAX=6: 永久保留上限, 超限自动裁最早加入
- 30 天内新币 + 24h vol > $1M, 自动加入监控池
- 推 QQ 仅在变化时 (与 v2.1 C 方案一致)
- references/: 新增 backtest-usage.md, change-driven-push.md, symbol-coverage-pitfall.md

用户原话: 新币最多保留六个, 每次扫描后筛选

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-10 19:43:21 +08:00

4.7 KiB
Raw Blame History

Change-Driven Push 推送策略细节 (v2.1)

okx_t_monitor.py v2.1 的核心: 只在有变化时推 QQ,其他静默。

📋 推送触发矩阵

触发条件 推送条目 频率
持仓变化 (abs(new_pos - old_pos) > 0.001) 🔄 "持仓: X → Y 张" 每次变化
价格触及 buy1/buy2/sell1/sell2 (距 < 0.5%) 📍 "价格触及 buy2=Z (距 W%)" 每次扫描
浮盈 ≥ 5% 且变化 ≥ 3% 📈/📉 "浮盈变化: X% → Y% (Z%)" 变化触发
做 T 实际成交 完整做T记录 (保留 v2.0 格式) 每次成交
做 T 失败 (API code != 0) 失败原因 每次失败
静默 (无以上 5 种) (本地 print 💤 静默:) 静默

🔧 实现细节 (代码视角)

find_nearest_level(price, levels, traded_levels) -> str|None

def find_nearest_level(price, levels, traded_levels):
    """找价格 0.5% 内最近的关键位, 跨 4 个候选 (buy2/buy1/sell1/sell2)"""
    threshold = 0.005  # 0.5%
    nearest = None
    min_dist = float('inf')
    for name in ['buy2', 'buy1', 'sell1', 'sell2']:
        if levels.get(name) is None:
            continue
        dist = abs(price - levels[name]) / price
        if dist < threshold and dist < min_dist:
            min_dist = dist
            nearest = name
    return nearest

关键: 同时检查 buy2 (距 -0.7×ATR) 和 sell1 (距 +0.49×ATR)。两个临界区都在 0.5% 内,只汇报最近的一个

check_changes(sym, price, pos_qty, avg_px, upl, levels, state) -> list[str]

def check_changes(sym, price, pos_qty, avg_px, upl, levels, state):
    """返回事件 list (空 list = 无变化)"""
    events = []
    # 1. 持仓变化
    prev_pos = state.get(f'{sym}_prev_pos')
    if prev_pos is not None and abs(pos_qty - prev_pos) > 0.001:
        events.append(f'🔄 持仓变化: {prev_pos:.2f}{pos_qty:.2f} 张')
    # 2. 价格触及
    nearest = find_nearest_level(price, levels, [])
    if nearest:
        level_price = levels[nearest]
        dist_pct = abs(price - level_price) / price * 100
        events.append(f'📍 价格触及 {nearest}={level_price:.2f} (距 {dist_pct:.2f}%)')
    # 3. 浮盈变化 (多空方向修正)
    if avg_px > 0 and pos_qty != 0:
        leverage = SYMBOL_SPECS.get(sym, {}).get('leverage', 25)
        pos_sign = 1 if pos_qty > 0 else -1
        upl_pct = (price - avg_px) / avg_px * 100 * leverage * pos_sign
        prev_upl_pct = state.get(f'{sym}_prev_upl_pct')
        if prev_upl_pct is not None and abs(upl_pct) >= 5:
            upl_diff = upl_pct - prev_upl_pct
            if abs(upl_diff) >= 3:
                emoji = '📈' if upl_diff > 0 else '📉'
                events.append(f'{emoji} 浮盈变化: {prev_upl_pct:.1f}% → {upl_pct:.1f}% ({upl_diff:+.1f}%)')
    return events

State 持久化

state 字典除了原有 traded_levels 之外,新增两类 key:

  • {sym}_prev_pos: 浮点数, 上次持仓张数
  • {sym}_prev_upl_pct: 浮点数 or None, 上次浮盈百分比

重要: 首次 cron 跑 这些 key 不存在, 不会触发变化 (因为没"上次"可比)。这是有意为之 — 不让首次运行就推"持仓从无 → 1.45 张"的噪音。

🚨 Pitfall

1. 浮盈方向

计算:(price - avg_px) / avg_px * 100 * leverage * pos_sign

pos_sign 是关键 — 空头pos_qty < 0, 价格跌时浮盈大, 所以乘 -1 让"价格下跌 = 浮盈增加"。

不乘 pos_sign 会把空头的 📈/📉 推反 — 价格跌了你说"📉 浮盈变小"是反的。

2. 静默不等于"系统没跑"

  • cron 触发 → monitor() 跑 → 无变化 → 本地 print 💤 静默: 有持仓但无变化 + 不推 QQ
  • 系统正常, 只是没新事件

监控 cron 故障排查: 看本地 print 日志 (cron 输出文件 ~/.hermes/cron/output/db03f9255ad0/)

3. 首次 cron 的"假静默"

  • 第 1 次跑: 没有 prev_pos 基准 → 全是"无变化" → 静默
  • 第 2 次跑起: 才有真正的变化检测

这是有意为之, 不是 bug。

🧪 验证测试

第一次部署改这版后, 建议:

  1. 手动 python3 ~/.hermes/scripts/crypto/okx_t_monitor.py 看本地输出
  2. 确认 cron 跑 5 次 (约 75 分钟) 后没推 QQ (静默)
  3. 触发一次手动市价调整 (App 改 1 张), 看下次 cron 跑是否推"持仓变化"

🔄 v2.1 升级步骤

如果你 fork 改了 v2.0 想升 v2.1:

  1. push_qq(msg) helper (提取 subprocess 调用)
  2. find_nearest_level() 函数
  3. check_changes() 函数
  4. 重构 monitor() 分两阶段:
    • Phase 1: 收集 syms_to_check, 跑变化检测, 推 QQ
    • Phase 2: 只对有持仓币种做 T
  5. 静默分支合并到最后

参考 crypto/okx_t_monitor.py 的 v2.1 实现。