"""100 USDT 的仓位在各币上能不能下出来——下单精度与最小量。 手工小额实盘的第一个坑不在策略,在交易规则。100 USDT 的仓位要拆成两半 (3 ATR 减半 50 USDT、8 ATR 目标 50 USDT),任一半低于最小下单量就下不出去, 或者被精度取整到与计划偏差很大的数量。 取整偏差会直接扭曲收益结构:若 50 USDT 被取整到 40,减半那一腿实际只出了 40%,剩余 60% 暴露在 8 ATR 目标上。回测的收益结构假设是 50/50。 python research/live/probe_rules.py --notional 100 """ from __future__ import annotations import argparse import asyncio import os import sys from decimal import Decimal from pathlib import Path HERE = Path(__file__).resolve() sys.path.insert(0, str(HERE.parents[1])) sys.path.insert(0, str(HERE.parent)) SYMS = os.environ.get( "SYMS", "BTC,ETH,SOL,BNB,XRP,DOGE,ADA,AVAX,LINK,LTC").split(",") async def run(notional: float) -> None: from hummingbot.connector.derivative.bitget_perpetual.bitget_perpetual_derivative import ( # noqa: E501 BitgetPerpetualDerivative, ) pairs = [f"{s}-USDT" for s in SYMS] conn = BitgetPerpetualDerivative( bitget_perpetual_api_key="", bitget_perpetual_secret_key="", bitget_perpetual_passphrase="", trading_pairs=pairs, trading_required=False) await conn.start_network() for _ in range(60): await asyncio.sleep(1) if conn.trading_rules and all(p in conn.trading_rules for p in pairs): break print(f"仓位 {notional:.0f} USDT · 减半腿 {notional / 2:.0f} USDT\n") print(f" {'币':<6}{'现价':>11}{'最小量':>12}{'量步长':>12}" f"{'最小名义':>10} 减半腿可行性") bad = [] for s in SYMS: p = f"{s}-USDT" r = conn.trading_rules.get(p) if r is None: print(f" {s:<6}{'取不到规则':>11}") continue ob = conn.get_order_book(p) px = float((ob.get_price(True) + ob.get_price(False)) / 2) if ob \ else float("nan") min_amt = float(r.min_order_size) step = float(r.min_base_amount_increment) min_not = float(r.min_notional_size or 0) half_base = (notional / 2) / px # 按步长向下取整——交易所就是这么处理的,向上取会下不出去 q = Decimal(str(half_base)) // Decimal(str(step)) * Decimal(str(step)) got = float(q) if got < min_amt or (min_not and got * px < min_not): verdict = f"⛔ 下不出(需 ≥ {max(min_amt, min_not / px):.6f})" bad.append(s) else: dev = abs(got * px - notional / 2) / (notional / 2) * 1e4 verdict = f"✓ {got:.6f} 币,偏差 {dev:.0f}bp" if dev > 100: verdict += " ⚠ 取整偏差大" bad.append(s) print(f" {s:<6}{px:>11,.4f}{min_amt:>12.6f}{step:>12.6f}" f"{min_not:>10.1f} {verdict}") print() if bad: print(f" ⛔ {notional:.0f} USDT 下这些币的减半腿有问题:{','.join(bad)}") print(f" 要么提高仓位,要么这些币不做减半、单腿到 8 ATR 出场——但后者") print(f" 改了回测的收益结构,不能直接套用原预算。") else: print(f" {notional:.0f} USDT 在全部 {len(SYMS)} 个币上都能拆成两半下出。") await conn.stop_network() def main() -> None: ap = argparse.ArgumentParser() ap.add_argument("--notional", type=float, default=100.0) a = ap.parse_args() asyncio.run(run(a.notional)) if __name__ == "__main__": main()