feat(okx-auto-position): v4.5.0 只设 SL 不设 TP, 平仓信号自动跟单

- scripts/okx_position_advisor.py execute_order():
  * 步骤名 tp_sl → sl_only
  * OCO algo (双触发) → conditional algo (单腿 SL)
  * 不挂 tpTriggerPx, 只挂 slTriggerPx
  * 实测: SPCX 1.45张 short OCO 已撤, 改挂 SL @ 153.62

- SKILL.md:
  * v4.5.0 章节: 只设止损不设止盈 (原因: advisor 算的 TP 太远, 占用保证金)
  * 平仓信号自动跟单: 同方向 reduceOnly 平仓, 反方向不动, 无 Y/N
  * 完全自动跟单流程: 加仓/新开仓/平仓/减仓信号都自动判断执行
  * 实测案例: SPCX 1.45张空 @ 149.15, 浮盈 /usr/bin/bash, 占用 45%

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-07-09 22:47:58 +08:00
co-authored by Claude
parent a437510a9b
commit feb1e73bc9
2 changed files with 260 additions and 1331 deletions
+241 -1309
View File
File diff suppressed because it is too large Load Diff
@@ -373,8 +373,8 @@ def format_recommendation(rec):
f"",
f"📊 方向: {rec['side_cn']} | 杠杆: **{rec['leverage']}x**",
f"📍 入场: **{rec['price']}**",
f"🛑 止损: **{rec['sl_price']}** (-{rec['sl_pct']}%)",
f"🎯 止盈: **{rec['tp_price']}** (+{rec['tp_pct']}%)",
f"🛑 止损: **{rec['sl_price']}** → 预亏 -{rec['sl_pnl']:.2f} USDT (保证金-{rec['sl_pnl']/max(rec['margin'], 0.01)*100:.0f}%)",
f"🎯 止盈: **{rec['tp_price']}** → 预盈 +{rec['tp_pnl']:.2f} USDT (保证金+{rec['tp_pnl']/max(rec['margin'], 0.01)*100:.0f}%)",
f"📐 盈亏比: **{rec['rr']}:1** ✅",
f"",
f"📦 张数: **{rec['contracts']}张** ({rec['base_amount']}个)",
@@ -389,8 +389,8 @@ def format_recommendation(rec):
f"",
f"📊 方向: {rec['side_cn']} | 杠杆: **{rec['leverage']}x**",
f"📍 入场: **{rec['price']}**",
f"🛑 止损: **{rec['sl_price']}** (-{rec['sl_pct']}%)",
f"🎯 止盈: **{rec['tp_price']}** (+{rec['tp_pct']}%)",
f"🛑 止损: **{rec['sl_price']}** → 预亏 -{rec['sl_pnl']:.2f} USDT (保证金-{rec['sl_pnl']/max(rec['margin'], 0.01)*100:.0f}%)",
f"🎯 止盈: **{rec['tp_price']}** → 预盈 +{rec['tp_pnl']:.2f} USDT (保证金+{rec['tp_pnl']/max(rec['margin'], 0.01)*100:.0f}%)",
f"📐 盈亏比: **{rec['rr']}:1** ⚠️",
f"",
f"📦 张数: **{rec['contracts']}张** ({rec['base_amount']}个)",
@@ -407,8 +407,8 @@ def format_recommendation(rec):
f"",
f"📊 方向: {rec['side_cn']} | 杠杆: **{rec['leverage']}x**",
f"📍 入场: **{rec['price']}**",
f"🛑 止损: **{rec['sl_price']}** (-{rec['sl_pct']}%)",
f"🎯 止盈: **{rec['tp_price']}** (+{rec['tp_pct']}%)",
f"🛑 止损: **{rec['sl_price']}** → 预亏 -{rec['sl_pnl']:.2f} USDT (保证金-{rec['sl_pnl']/max(rec['margin'], 0.01)*100:.0f}%)",
f"🎯 止盈: **{rec['tp_price']}** → 预盈 +{rec['tp_pnl']:.2f} USDT (保证金+{rec['tp_pnl']/max(rec['margin'], 0.01)*100:.0f}%)",
f"📐 盈亏比: **{rec['rr']}:1** ❌",
f"",
f"{cost_check.get('reason', '')}",
@@ -486,38 +486,34 @@ def execute_order(exchange, rec):
results['steps'].append({'step': 'cancel_old_algos', 'status': 'ok', 'cancelled': cancelled})
time.sleep(0.5) # wait for cancellation to propagate
# 5. Set TP/SL via OCO algo order
# 5. Set SL-only via conditional algo order (v4.5.0: 不设止盈, 靠平仓信号平仓)
try:
# For OCO: tpOrdPx=-1 and slOrdPx=-1 means market order on trigger
# 用单腿 conditional algo, 只挂止损
# 多头: 价格跌破 SL 时市价平仓
# 空头: 价格涨破 SL 时市价平仓
if side == 'sell':
# Short: TP trigger below, SL trigger above
# Short: SL trigger above entry
algo_params = {
'instId': inst_id,
'tdMode': 'cross',
'side': 'buy', # buy to close short
'posSide': 'net',
'ordType': 'oco',
'ordType': 'conditional',
'sz': str(contracts),
'tpTriggerPx': str(rec['tp_price']),
'tpOrdPx': '-1',
'tpTriggerPxType': 'last',
'slTriggerPx': str(rec['sl_price']),
'slOrdPx': '-1',
'slTriggerPxType': 'last',
'reduceOnly': 'true',
}
else:
# Long: TP trigger above, SL trigger below
# Long: SL trigger below entry
algo_params = {
'instId': inst_id,
'tdMode': 'cross',
'side': 'sell', # sell to close long
'posSide': 'net',
'ordType': 'oco',
'ordType': 'conditional',
'sz': str(contracts),
'tpTriggerPx': str(rec['tp_price']),
'tpOrdPx': '-1',
'tpTriggerPxType': 'last',
'slTriggerPx': str(rec['sl_price']),
'slOrdPx': '-1',
'slTriggerPxType': 'last',
@@ -527,12 +523,13 @@ def execute_order(exchange, rec):
resp = exchange.private_post_trade_order_algo(algo_params)
if resp.get('data') and resp['data'][0].get('algoId'):
algo_id = resp['data'][0]['algoId']
results['algo'] = {'id': algo_id, 'tp': rec['tp_price'], 'sl': rec['sl_price']}
results['steps'].append({'step': 'tp_sl', 'status': 'ok', 'algo_id': algo_id})
# v4.5.0: 只设 SL, tp 标记为 None
results['algo'] = {'id': algo_id, 'sl': rec['sl_price'], 'tp': None}
results['steps'].append({'step': 'sl_only', 'status': 'ok', 'algo_id': algo_id})
else:
results['steps'].append({'step': 'tp_sl', 'status': 'warn', 'msg': str(resp)})
results['steps'].append({'step': 'sl_only', 'status': 'warn', 'msg': str(resp)})
except Exception as e:
results['steps'].append({'step': 'tp_sl', 'status': 'error', 'msg': str(e)})
results['steps'].append({'step': 'sl_only', 'status': 'error', 'msg': str(e)})
# 5. Verify position
try: