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:
+241
-1309
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:
|
||||
|
||||
Reference in New Issue
Block a user