diff --git a/research/live/deploy/start.sh b/research/live/deploy/start.sh index 7dfc1a1..8d90fca 100755 --- a/research/live/deploy/start.sh +++ b/research/live/deploy/start.sh @@ -116,7 +116,8 @@ docker run -d --name "$NAME" -w /home/hummingbot \ -e SHADOW_INCR="${SHADOW_INCR:-1}" \ -e TG_TOKEN="${TG_TOKEN:-}" \ -e TG_CHAT="${TG_CHAT:-}" \ - -e TG_NOTIONAL="${TG_NOTIONAL:-200}" \ + -e TG_NOTIONAL="${TG_NOTIONAL:-500}" \ + -e TG_LEVERAGE="${TG_LEVERAGE:-10}" \ -e TG_STALE_S="${TG_STALE_S:-90}" \ -v "$REPO_ROOT:/repo:ro" \ -v "$OUT:/out" \ diff --git a/research/live/deploy/tg.env.example b/research/live/deploy/tg.env.example index d6054b3..bb63692 100644 --- a/research/live/deploy/tg.env.example +++ b/research/live/deploy/tg.env.example @@ -6,8 +6,15 @@ # 返回的 result[0].message.chat.id 就是 export TG_TOKEN="" export TG_CHAT="" -# 每笔名义额(USDT)。小额实盘先用小的,它只影响推送里的下单数量 -export TG_NOTIONAL="200" +# 每笔名义额(USDT)。杠杆**不改**手续费与滑点(都按名义额收),所以抬名义额 +# 是有真实成本的;抬它的唯一理由是压掉步长取整:实测最差币的偏差 +# 100U → 6.7%(SOL)、500U → 1.8%、1000U → 0.6%。 +export TG_NOTIONAL="500" +# 杠杆只影响占用保证金,不影响名义敞口/手续费/滑点/盈亏绝对值。 +# 名义 500 在 10x 下占 50 USDT 保证金;止损在 2 ATR ≈ 0.2%,而 10x 强平约需 +# 逆向 10% = 100 个 ATR,差 50 倍,所以这里的杠杆几乎不引入强平风险。 +# 交易所侧记得设**逐仓**,让每笔最大损失被保证金封住。 +export TG_LEVERAGE="10" # 距「参考价成立」超过这么多秒就标为已失效。参考价是次根开盘价, # 过了就不是回测那个成交价了 export TG_STALE_S="90" diff --git a/research/live/tg_notify.py b/research/live/tg_notify.py index d7d9d0b..de85f58 100644 --- a/research/live/tg_notify.py +++ b/research/live/tg_notify.py @@ -33,7 +33,14 @@ import time TOKEN = os.environ.get("TG_TOKEN", "") CHAT = os.environ.get("TG_CHAT", "") -NOTIONAL = float(os.environ.get("TG_NOTIONAL", "200")) +# 名义额。杠杆不改手续费与滑点(都按名义额收),所以抬名义额是有真实成本的; +# 抬它的理由只有一个:把步长取整压下去。实测最差币的偏差 +# 100U → 6.7%(SOL)、500U → 1.8%、1000U → 0.6%。 +NOTIONAL = float(os.environ.get("TG_NOTIONAL", "500")) +# 杠杆只影响占用保证金,不影响名义敞口、手续费、滑点、盈亏绝对值。 +# 止损在 2 ATR ≈ 0.2%,而 10x 的强平约需逆向 10% = 100 个 ATR,差 50 倍, +# 所以这里的杠杆几乎不引入强平风险。逐仓,让每笔的最大损失被保证金封住。 +LEVERAGE = float(os.environ.get("TG_LEVERAGE", "10")) STALE_S = float(os.environ.get("TG_STALE_S", "90")) ENABLED = bool(TOKEN and CHAT) @@ -140,11 +147,16 @@ def build(sym: str, direction: int, entry: float, atr_pct: float, qty, half = quantize(NOTIONAL, entry, rule) tick = rule["tick"] lv = {k: snap_px(v, tick) for k, v in lv.items()} - qty_line = (f"入场 {qty:.6g} 币 ≈ {qty * entry:,.1f} USDT" + notional = qty * entry + qty_line = (f"入场 {qty:.6g} 币 ≈ {notional:,.1f} USDT" f" · 减半腿 {half:.6g} 币(正好一半)") else: qty = NOTIONAL / entry + notional = NOTIONAL qty_line = f"入场 {qty:.6g} 币 ≈ {NOTIONAL:,.0f} USDT(未取整)" + margin = notional / LEVERAGE if LEVERAGE > 0 else notional + # 止损距入场 2 ATR,换成保证金的百分比才是「这笔最多亏多少本金」 + loss_pct = SL_ATR * atr_pct * LEVERAGE * 100 head = f"⛔ 已失效({age_s:.0f}s > {STALE_S:.0f}s)· 不要入场" if stale \ else f"✅ {side} {sym}" @@ -153,6 +165,9 @@ def build(sym: str, direction: int, entry: float, atr_pct: float, "", f"参考成交价 {_fmt(lv['entry'])} ← 回测口径(次根开盘)", qty_line, + f"{LEVERAGE:.0f}x 逐仓 → 占用保证金 {margin:,.1f} USDT" + f" · 触止损亏 {notional * SL_ATR * atr_pct:,.2f} USDT" + f"(保证金的 {loss_pct:.1f}%)", f"距参考价成立 {age_s:.1f}s(含数据延迟 {lag_ms:.0f}ms,不可压缩)", "", f"止损 {_fmt(lv['stop'])} (2 ATR,stop-market,全仓)",