feat(position_manager, config): 添加早止盈功能以优化山寨币交易策略

在 `position_manager.py` 中实现了早止盈逻辑,允许在盈利达到一定比例且持仓时间超过指定小时数时,自动市价止盈,避免反转风险。同时,在 `config.py` 中新增相关配置项以支持该功能,提升了交易策略的灵活性与风险控制能力。
This commit is contained in:
薇薇安 2026-02-20 00:33:28 +08:00
parent 43daa922a4
commit d31c44a22a
3 changed files with 31 additions and 703 deletions

View File

@ -198,7 +198,7 @@ DEFAULT_TRADING_CONFIG = {
'EXCLUDE_MAJOR_COINS': True, # 排除主流币
'STOP_LOSS_PERCENT': 0.05, # 基础止损5%配合ATR动态止损作为保底
'TAKE_PROFIT_PERCENT': 0.25, # 第二目标止盈25%(略拉近止盈以提高触达率)
'TAKE_PROFIT_1_PERCENT': 0.15, # 第一目标止盈15%(更容易先锁定部分利润
'TAKE_PROFIT_1_PERCENT': 0.12, # 第一目标止盈12%(山寨币不必久拿,早点落袋为安,降低反转风险
'MIN_RR_FOR_TP1': 1.2, # 第一目标止盈的最小盈亏比(相对于止损距离),略放宽以提高触发率
'MIN_STOP_LOSS_PRICE_PCT': 0.03, # 最小止损价格变动3%(适当放宽止损,减少正常波动被扫)
'MIN_TAKE_PROFIT_PRICE_PCT': 0.02, # 最小止盈价格变动2%
@ -253,6 +253,10 @@ DEFAULT_TRADING_CONFIG = {
'TIME_STOP_MAX_HOLD_HOURS': 8, # 持仓超过 N 小时且未达最小盈利则考虑平仓(低波动期更适用)
'TIME_STOP_MIN_PNL_PCT_TO_HOLD': 0.0, # 持仓超过时间阈值时,若 pnl% 低于此值则平仓0=允许小亏即走)
'TIME_STOP_ENABLED': True, # 是否启用时间/无盈利止损(可与 regime 结合:仅低波动期或始终启用)
# 山寨币早止盈:盈利达标且持仓满 N 小时则市价止盈,不等挂单 TP避免反转回吐
'EARLY_TAKE_PROFIT_ENABLED': True, # 是否启用「盈利达标+持仓时长」早止盈
'EARLY_TAKE_PROFIT_MIN_PNL_PCT': 10.0, # 盈利达到此比例(% of margin即符合条件默认 10%
'EARLY_TAKE_PROFIT_MIN_HOLD_HOURS': 2.0, # 且持仓至少 N 小时才触发(避免刚开仓就平)
# 开仓前流动性检查(价差/深度):低量期可减少滑点与冲击成本
'LIQUIDITY_CHECK_BEFORE_ENTRY': False, # 默认关闭;开启后开仓前检查盘口价差与深度,不通过则跳过
'LIQUIDITY_MAX_SPREAD_PCT': 0.005, # 买一卖一价差超过此比例0.5%)则跳过

View File

@ -1985,6 +1985,32 @@ class PositionManager:
except Exception as e:
logger.debug(f"从Redis重新加载配置失败: {e}")
# 山寨币早止盈:盈利达标且持仓满 N 小时则市价止盈,不必等挂单 TP避免反转回吐
early_tp_enabled = config.TRADING_CONFIG.get('EARLY_TAKE_PROFIT_ENABLED', True)
if early_tp_enabled and pnl_percent_margin > 0:
min_pnl_pct = float(config.TRADING_CONFIG.get('EARLY_TAKE_PROFIT_MIN_PNL_PCT', 10) or 10)
min_hold_hours = float(config.TRADING_CONFIG.get('EARLY_TAKE_PROFIT_MIN_HOLD_HOURS', 2) or 2)
hold_hours = 0.0
entry_time = position_info.get('entryTime')
if entry_time is not None:
try:
if isinstance(entry_time, datetime):
hold_hours = (get_beijing_time() - entry_time).total_seconds() / 3600.0
elif isinstance(entry_time, str) and len(entry_time) >= 19:
entry_dt = datetime.strptime(entry_time[:19], '%Y-%m-%d %H:%M:%S')
hold_hours = (get_beijing_time() - entry_dt).total_seconds() / 3600.0
else:
hold_hours = (time.time() - (float(entry_time) if isinstance(entry_time, (int, float)) else 0)) / 3600.0
except Exception:
pass
if pnl_percent_margin >= min_pnl_pct and hold_hours >= min_hold_hours:
logger.info(
f"{symbol} [早止盈] 盈利{pnl_percent_margin:.2f}%≥{min_pnl_pct}% 且持仓{hold_hours:.1f}h≥{min_hold_hours}h市价止盈离场山寨币不必久拿"
)
if await self.close_position(symbol, reason='early_take_profit'):
closed_positions.append(symbol)
continue
# 检查是否启用移动止损默认False需要显式启用
use_trailing = config.TRADING_CONFIG.get('USE_TRAILING_STOP', False)
if use_trailing:

View File

@ -1,702 +0,0 @@
[
{
"id": null,
"symbol": "ZECUSDT",
"side": "SELL",
"quantity": 0.112,
"entry_price": 265.8,
"entry_value_usdt": 29.7696,
"notional_usdt": 29.12784,
"margin_usdt": 7.28196,
"original_notional_usdt": null,
"original_margin_usdt": null,
"mark_price": 260.07,
"pnl": 0.64176,
"pnl_percent": 8.813011881416543,
"leverage": 4,
"entry_time": null,
"stop_loss_price": null,
"take_profit_price": null,
"take_profit_1": null,
"take_profit_2": null,
"atr": null,
"entry_order_id": null,
"entry_order_type": null,
"open_orders": [
{
"orderId": 2000000479271333,
"type": "TAKE_PROFIT_MARKET",
"side": "BUY",
"stopPrice": 239.04,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
},
{
"orderId": 2000000479271328,
"type": "STOP_MARKET",
"side": "BUY",
"stopPrice": 273.77,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
}
],
"active_sl_orders": "STOP_MARKET @ 273.77 (NEW)",
"active_tp_orders": "TAKE_PROFIT_MARKET @ 239.04 (NEW)",
"binance_open_orders_raw": [
{
"orderId": 2000000479271333,
"type": "TAKE_PROFIT_MARKET",
"side": "BUY",
"stopPrice": 239.04,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
},
{
"orderId": 2000000479271328,
"type": "STOP_MARKET",
"side": "BUY",
"stopPrice": 273.77,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
}
]
},
{
"id": null,
"symbol": "HYPEUSDT",
"side": "SELL",
"quantity": 0.96,
"entry_price": 28.503,
"entry_value_usdt": 27.36288,
"notional_usdt": 27.712493712,
"margin_usdt": 6.928123428,
"original_notional_usdt": null,
"original_margin_usdt": null,
"mark_price": 28.86718095,
"pnl": -0.34961371,
"pnl_percent": -5.046297365128294,
"leverage": 4,
"entry_time": null,
"stop_loss_price": null,
"take_profit_price": null,
"take_profit_1": null,
"take_profit_2": null,
"atr": null,
"entry_order_id": null,
"entry_order_type": null,
"open_orders": [
{
"orderId": 2000000479340803,
"type": "TAKE_PROFIT_MARKET",
"side": "BUY",
"stopPrice": 26.636,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
},
{
"orderId": 2000000479340801,
"type": "STOP_MARKET",
"side": "BUY",
"stopPrice": 29.358,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
}
],
"active_sl_orders": "STOP_MARKET @ 29.358 (NEW)",
"active_tp_orders": "TAKE_PROFIT_MARKET @ 26.636 (NEW)",
"binance_open_orders_raw": [
{
"orderId": 2000000479340803,
"type": "TAKE_PROFIT_MARKET",
"side": "BUY",
"stopPrice": 26.636,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
},
{
"orderId": 2000000479340801,
"type": "STOP_MARKET",
"side": "BUY",
"stopPrice": 29.358,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
}
]
},
{
"id": null,
"symbol": "TAOUSDT",
"side": "SELL",
"quantity": 0.211,
"entry_price": 185.4614691943,
"entry_value_usdt": 39.1323699999973,
"notional_usdt": 38.51799427279,
"margin_usdt": 9.6294985681975,
"original_notional_usdt": null,
"original_margin_usdt": null,
"mark_price": 182.54973589,
"pnl": 0.61437572,
"pnl_percent": 6.38014238902371,
"leverage": 4,
"entry_time": null,
"stop_loss_price": null,
"take_profit_price": null,
"take_profit_1": null,
"take_profit_2": null,
"atr": null,
"entry_order_id": null,
"entry_order_type": null,
"open_orders": [
{
"orderId": 2000000480794655,
"type": "TAKE_PROFIT_MARKET",
"side": "BUY",
"stopPrice": 170.49,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
},
{
"orderId": 2000000480794649,
"type": "STOP_MARKET",
"side": "BUY",
"stopPrice": 189.07,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
}
],
"active_sl_orders": "STOP_MARKET @ 189.07 (NEW)",
"active_tp_orders": "TAKE_PROFIT_MARKET @ 170.49 (NEW)",
"binance_open_orders_raw": [
{
"orderId": 2000000480794655,
"type": "TAKE_PROFIT_MARKET",
"side": "BUY",
"stopPrice": 170.49,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
},
{
"orderId": 2000000480794649,
"type": "STOP_MARKET",
"side": "BUY",
"stopPrice": 189.07,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
}
]
},
{
"id": null,
"symbol": "FILUSDT",
"side": "SELL",
"quantity": 60,
"entry_price": 0.933,
"entry_value_usdt": 55.980000000000004,
"notional_usdt": 55.552446,
"margin_usdt": 13.8881115,
"original_notional_usdt": null,
"original_margin_usdt": null,
"mark_price": 0.9258741,
"pnl": 0.427554,
"pnl_percent": 3.078561113222629,
"leverage": 4,
"entry_time": null,
"stop_loss_price": null,
"take_profit_price": null,
"take_profit_1": null,
"take_profit_2": null,
"atr": null,
"entry_order_id": null,
"entry_order_type": null,
"open_orders": [
{
"orderId": 2000000479413824,
"type": "TAKE_PROFIT_MARKET",
"side": "BUY",
"stopPrice": 0.875,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
},
{
"orderId": 2000000479413817,
"type": "STOP_MARKET",
"side": "BUY",
"stopPrice": 0.96,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
}
],
"active_sl_orders": "STOP_MARKET @ 0.96 (NEW)",
"active_tp_orders": "TAKE_PROFIT_MARKET @ 0.875 (NEW)",
"binance_open_orders_raw": [
{
"orderId": 2000000479413824,
"type": "TAKE_PROFIT_MARKET",
"side": "BUY",
"stopPrice": 0.875,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
},
{
"orderId": 2000000479413817,
"type": "STOP_MARKET",
"side": "BUY",
"stopPrice": 0.96,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
}
]
},
{
"id": null,
"symbol": "TRUMPUSDT",
"side": "SELL",
"quantity": 7.2,
"entry_price": 3.436,
"entry_value_usdt": 24.7392,
"notional_usdt": 24.624,
"margin_usdt": 6.156,
"original_notional_usdt": null,
"original_margin_usdt": null,
"mark_price": 3.42,
"pnl": 0.1152,
"pnl_percent": 1.8713450292397662,
"leverage": 4,
"entry_time": null,
"stop_loss_price": null,
"take_profit_price": null,
"take_profit_1": null,
"take_profit_2": null,
"atr": null,
"entry_order_id": null,
"entry_order_type": null,
"open_orders": [
{
"orderId": 2000000479508471,
"type": "TAKE_PROFIT_MARKET",
"side": "BUY",
"stopPrice": 3.19,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
},
{
"orderId": 2000000479508466,
"type": "STOP_MARKET",
"side": "BUY",
"stopPrice": 3.539,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
}
],
"active_sl_orders": "STOP_MARKET @ 3.539 (NEW)",
"active_tp_orders": "TAKE_PROFIT_MARKET @ 3.19 (NEW)",
"binance_open_orders_raw": [
{
"orderId": 2000000479508471,
"type": "TAKE_PROFIT_MARKET",
"side": "BUY",
"stopPrice": 3.19,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
},
{
"orderId": 2000000479508466,
"type": "STOP_MARKET",
"side": "BUY",
"stopPrice": 3.539,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
}
]
},
{
"id": null,
"symbol": "PUMPUSDT",
"side": "SELL",
"quantity": 9729,
"entry_price": 0.002039,
"entry_value_usdt": 19.837431,
"notional_usdt": 19.934721,
"margin_usdt": 4.98368025,
"original_notional_usdt": null,
"original_margin_usdt": null,
"mark_price": 0.002049,
"pnl": -0.09729,
"pnl_percent": -1.9521717911176184,
"leverage": 4,
"entry_time": null,
"stop_loss_price": null,
"take_profit_price": null,
"take_profit_1": null,
"take_profit_2": null,
"atr": null,
"entry_order_id": null,
"entry_order_type": null,
"open_orders": [
{
"orderId": 2000000480154344,
"type": "TAKE_PROFIT_MARKET",
"side": "BUY",
"stopPrice": 0.001851,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
},
{
"orderId": 2000000480154340,
"type": "STOP_MARKET",
"side": "BUY",
"stopPrice": 0.0021,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
}
],
"active_sl_orders": "STOP_MARKET @ 0.0021 (NEW)",
"active_tp_orders": "TAKE_PROFIT_MARKET @ 0.001851 (NEW)",
"binance_open_orders_raw": [
{
"orderId": 2000000480154344,
"type": "TAKE_PROFIT_MARKET",
"side": "BUY",
"stopPrice": 0.001851,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
},
{
"orderId": 2000000480154340,
"type": "STOP_MARKET",
"side": "BUY",
"stopPrice": 0.0021,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
}
]
},
{
"id": null,
"symbol": "NAORISUSDT",
"side": "BUY",
"quantity": 335,
"entry_price": 0.0376422985075,
"entry_value_usdt": 12.610170000012499,
"notional_usdt": 13.52641225,
"margin_usdt": 13.52641225,
"original_notional_usdt": null,
"original_margin_usdt": null,
"mark_price": 0.04037735,
"pnl": 0.9162451,
"pnl_percent": 6.773748153358257,
"leverage": 1,
"entry_time": null,
"stop_loss_price": null,
"take_profit_price": null,
"take_profit_1": null,
"take_profit_2": null,
"atr": null,
"entry_order_id": null,
"entry_order_type": null,
"open_orders": [
{
"orderId": 2000000480529814,
"type": "TAKE_PROFIT_MARKET",
"side": "SELL",
"stopPrice": 0.06148,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
},
{
"orderId": 2000000480529809,
"type": "STOP_MARKET",
"side": "SELL",
"stopPrice": 0.03643,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
}
],
"active_sl_orders": "STOP_MARKET @ 0.03643 (NEW)",
"active_tp_orders": "TAKE_PROFIT_MARKET @ 0.06148 (NEW)",
"binance_open_orders_raw": [
{
"orderId": 2000000480529814,
"type": "TAKE_PROFIT_MARKET",
"side": "SELL",
"stopPrice": 0.06148,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
},
{
"orderId": 2000000480529809,
"type": "STOP_MARKET",
"side": "SELL",
"stopPrice": 0.03643,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
}
]
},
{
"id": null,
"symbol": "KITEUSDT",
"side": "BUY",
"quantity": 617,
"entry_price": 0.2258499027553,
"entry_value_usdt": 139.3493900000201,
"notional_usdt": 141.32854537,
"margin_usdt": 35.3321363425,
"original_notional_usdt": null,
"original_margin_usdt": null,
"mark_price": 0.22905761,
"pnl": 1.97915707,
"pnl_percent": 5.601577699164852,
"leverage": 4,
"entry_time": null,
"stop_loss_price": null,
"take_profit_price": null,
"take_profit_1": null,
"take_profit_2": null,
"atr": null,
"entry_order_id": null,
"entry_order_type": null,
"open_orders": [
{
"orderId": 2000000480845123,
"type": "TAKE_PROFIT_MARKET",
"side": "SELL",
"stopPrice": 0.26007,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
},
{
"orderId": 2000000480845117,
"type": "STOP_MARKET",
"side": "SELL",
"stopPrice": 0.22204,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
}
],
"active_sl_orders": "STOP_MARKET @ 0.22204 (NEW)",
"active_tp_orders": "TAKE_PROFIT_MARKET @ 0.26007 (NEW)",
"binance_open_orders_raw": [
{
"orderId": 2000000480845123,
"type": "TAKE_PROFIT_MARKET",
"side": "SELL",
"stopPrice": 0.26007,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
},
{
"orderId": 2000000480845117,
"type": "STOP_MARKET",
"side": "SELL",
"stopPrice": 0.22204,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
}
]
},
{
"id": null,
"symbol": "AAVEUSDT",
"side": "SELL",
"quantity": 0.5,
"entry_price": 123.936,
"entry_value_usdt": 61.968,
"notional_usdt": 61.495,
"margin_usdt": 15.37375,
"original_notional_usdt": null,
"original_margin_usdt": null,
"mark_price": 122.99,
"pnl": 0.473,
"pnl_percent": 3.0766729002357915,
"leverage": 4,
"entry_time": null,
"stop_loss_price": null,
"take_profit_price": null,
"take_profit_1": null,
"take_profit_2": null,
"atr": null,
"entry_order_id": null,
"entry_order_type": null,
"open_orders": [
{
"orderId": 2000000480581320,
"type": "TAKE_PROFIT_MARKET",
"side": "BUY",
"stopPrice": 115.95,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
},
{
"orderId": 2000000480581308,
"type": "STOP_MARKET",
"side": "BUY",
"stopPrice": 127.69,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
}
],
"active_sl_orders": "STOP_MARKET @ 127.69 (NEW)",
"active_tp_orders": "TAKE_PROFIT_MARKET @ 115.95 (NEW)",
"binance_open_orders_raw": [
{
"orderId": 2000000480581320,
"type": "TAKE_PROFIT_MARKET",
"side": "BUY",
"stopPrice": 115.95,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
},
{
"orderId": 2000000480581308,
"type": "STOP_MARKET",
"side": "BUY",
"stopPrice": 127.69,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
}
]
},
{
"id": null,
"symbol": "ASTERUSDT",
"side": "SELL",
"quantity": 84,
"entry_price": 0.69645,
"entry_value_usdt": 58.5018,
"notional_usdt": 58.576746480000004,
"margin_usdt": 14.644186620000001,
"original_notional_usdt": null,
"original_margin_usdt": null,
"mark_price": 0.69734222,
"pnl": -0.07494648,
"pnl_percent": -0.5117831528972961,
"leverage": 4,
"entry_time": null,
"stop_loss_price": null,
"take_profit_price": null,
"take_profit_1": null,
"take_profit_2": null,
"atr": null,
"entry_order_id": null,
"entry_order_type": null,
"open_orders": [
{
"orderId": 2000000480743935,
"type": "TAKE_PROFIT_MARKET",
"side": "BUY",
"stopPrice": 0.6525,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
},
{
"orderId": 2000000480743932,
"type": "STOP_MARKET",
"side": "BUY",
"stopPrice": 0.7168,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
}
],
"active_sl_orders": "STOP_MARKET @ 0.7168 (NEW)",
"active_tp_orders": "TAKE_PROFIT_MARKET @ 0.6525 (NEW)",
"binance_open_orders_raw": [
{
"orderId": 2000000480743935,
"type": "TAKE_PROFIT_MARKET",
"side": "BUY",
"stopPrice": 0.6525,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
},
{
"orderId": 2000000480743932,
"type": "STOP_MARKET",
"side": "BUY",
"stopPrice": 0.7168,
"price": 0,
"origType": "CONDITIONAL",
"reduceOnly": true,
"status": "NEW"
}
]
}
]