1
This commit is contained in:
parent
c4a23be3bf
commit
e024bf8ebe
|
|
@ -312,6 +312,9 @@ DEFAULT_TRADING_CONFIG = {
|
|||
# ===== 系统单标识(用于同步时区分本系统开仓 vs 手动开仓)=====
|
||||
# 下单时写入 newClientOrderId = SYSTEM_ORDER_ID_PREFIX_时间戳_随机,同步/补建时根据订单 clientOrderId 前缀判断是否系统单
|
||||
'SYSTEM_ORDER_ID_PREFIX': 'SYS',
|
||||
# ===== 仅币安有仓时的监管策略 =====
|
||||
# 当 SYNC_CREATE_MANUAL_ENTRY_RECORD=False 时,仍对「仅币安有仓且存在止损/止盈条件单」的持仓接入监控并补建记录(多为系统单)
|
||||
'SYNC_MONITOR_BINANCE_POSITIONS_WITH_SLTP': True,
|
||||
}
|
||||
|
||||
def _get_trading_config():
|
||||
|
|
|
|||
|
|
@ -1293,6 +1293,21 @@ class PositionManager:
|
|||
q = round(q, qty_precision)
|
||||
return max(0.0, q)
|
||||
|
||||
async def _symbol_has_sltp_orders(self, symbol: str) -> bool:
|
||||
"""该交易对在交易所是否存在止损/止盈类条件单(用于识别「可能为系统单」的持仓)。"""
|
||||
try:
|
||||
for o in (await self.client.get_open_orders(symbol)) or []:
|
||||
t = str(o.get("type") or "").upper()
|
||||
if t in ("STOP_MARKET", "TAKE_PROFIT_MARKET", "STOP", "TAKE_PROFIT"):
|
||||
return True
|
||||
for o in (await self.client.futures_get_open_algo_orders(symbol, algo_type="CONDITIONAL")) or []:
|
||||
t = str(o.get("orderType") or o.get("type") or "").upper()
|
||||
if t in ("STOP_MARKET", "TAKE_PROFIT_MARKET", "STOP", "TAKE_PROFIT"):
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
|
||||
async def _ensure_exchange_sltp_orders(self, symbol: str, position_info: Dict, current_price: Optional[float] = None) -> None:
|
||||
"""
|
||||
在币安侧挂止损/止盈保护单(STOP_MARKET + TAKE_PROFIT_MARKET)。
|
||||
|
|
@ -2844,22 +2859,6 @@ class PositionManager:
|
|||
sync_recover = config.TRADING_CONFIG.get("SYNC_RECOVER_MISSING_POSITIONS", True)
|
||||
sync_recover_only_has_sltp = config.TRADING_CONFIG.get("SYNC_RECOVER_ONLY_WHEN_HAS_SLTP", True)
|
||||
|
||||
def _order_is_sltp(o, type_key="type"):
|
||||
t = str(o.get(type_key) or o.get("orderType") or "").upper()
|
||||
return t in ("STOP_MARKET", "TAKE_PROFIT_MARKET", "STOP", "TAKE_PROFIT")
|
||||
|
||||
async def _symbol_has_sltp(sym):
|
||||
try:
|
||||
for o in (await self.client.get_open_orders(sym)) or []:
|
||||
if _order_is_sltp(o, "type"):
|
||||
return True
|
||||
for o in (await self.client.futures_get_open_algo_orders(sym, algo_type="CONDITIONAL")) or []:
|
||||
if _order_is_sltp(o, "orderType"):
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
|
||||
if sync_recover:
|
||||
system_order_prefix = (config.TRADING_CONFIG.get("SYSTEM_ORDER_ID_PREFIX") or "").strip()
|
||||
if system_order_prefix:
|
||||
|
|
@ -2907,7 +2906,7 @@ class PositionManager:
|
|||
# 无法获取订单或 cid 为空(历史单/未带前缀)时不视为手动单,继续补建
|
||||
else:
|
||||
# 未配置前缀时,用「是否有止损/止盈单」区分
|
||||
if sync_recover_only_has_sltp and not (await _symbol_has_sltp(symbol)):
|
||||
if sync_recover_only_has_sltp and not (await self._symbol_has_sltp_orders(symbol)):
|
||||
logger.debug(f" {symbol} 无止损/止盈单,跳过补建")
|
||||
continue
|
||||
if is_clearly_manual:
|
||||
|
|
@ -3146,18 +3145,25 @@ class PositionManager:
|
|||
logger.info(f"本地持仓记录: {len(active_symbols)} 个 ({', '.join(active_symbols) if active_symbols else '无'})")
|
||||
|
||||
# 仅为本系统已有记录的持仓启动监控;若未开启「同步创建手动开仓记录」,则不为「仅币安有仓」创建临时记录或监控
|
||||
# 例外:SYNC_MONITOR_BINANCE_POSITIONS_WITH_SLTP=True 时,对「仅币安有仓且存在止损/止盈单」的视为可监管(多为系统单),补建并监控
|
||||
only_binance = binance_symbols - active_symbols
|
||||
if only_binance and not sync_create_manual:
|
||||
logger.info(f"跳过 {len(only_binance)} 个仅币安持仓的监控(SYNC_CREATE_MANUAL_ENTRY_RECORD=False): {', '.join(only_binance)}")
|
||||
monitor_binance_with_sltp = config.TRADING_CONFIG.get("SYNC_MONITOR_BINANCE_POSITIONS_WITH_SLTP", True)
|
||||
if only_binance and not sync_create_manual and not monitor_binance_with_sltp:
|
||||
logger.info(f"跳过 {len(only_binance)} 个仅币安持仓的监控(SYNC_CREATE_MANUAL_ENTRY_RECORD=False 且 SYNC_MONITOR_BINANCE_POSITIONS_WITH_SLTP=False): {', '.join(only_binance)}")
|
||||
|
||||
for position in positions:
|
||||
symbol = position['symbol']
|
||||
if symbol not in self._monitor_tasks:
|
||||
# 若不在 active_positions 且未开启「同步创建手动开仓记录」,不创建临时记录、不为其启动监控
|
||||
# 若不在 active_positions:要么开启「同步创建手动开仓」则全部接入,要么仅对「有止损/止盈单」的接入(视为系统单)
|
||||
if symbol not in self.active_positions:
|
||||
if not sync_create_manual:
|
||||
has_sltp = await self._symbol_has_sltp_orders(symbol) if monitor_binance_with_sltp else False
|
||||
should_create = sync_create_manual or (monitor_binance_with_sltp and has_sltp)
|
||||
if not should_create:
|
||||
continue
|
||||
logger.warning(f"{symbol} 在币安有持仓但不在本地记录中,可能是手动开仓,尝试创建记录...")
|
||||
if sync_create_manual:
|
||||
logger.warning(f"{symbol} 在币安有持仓但不在本地记录中,可能是手动开仓,尝试创建记录...")
|
||||
else:
|
||||
logger.info(f"{symbol} 仅币安有仓且存在止损/止盈单,按系统单接入监控并补建记录")
|
||||
try:
|
||||
entry_price = position.get('entryPrice', 0)
|
||||
position_amt = position['positionAmt']
|
||||
|
|
@ -3195,6 +3201,7 @@ class PositionManager:
|
|||
take_profit_pct=take_profit_pct_margin
|
||||
)
|
||||
|
||||
entry_reason = 'manual_entry_temp' if sync_create_manual else 'sync_recovered'
|
||||
position_info = {
|
||||
'symbol': symbol,
|
||||
'side': side,
|
||||
|
|
@ -3207,13 +3214,33 @@ class PositionManager:
|
|||
'takeProfit': take_profit_price,
|
||||
'initialStopLoss': stop_loss_price,
|
||||
'leverage': leverage,
|
||||
'entryReason': 'manual_entry_temp',
|
||||
'entryReason': entry_reason,
|
||||
'atr': None,
|
||||
'maxProfit': 0.0,
|
||||
'trailingStopActivated': False
|
||||
}
|
||||
if not sync_create_manual and DB_AVAILABLE and Trade:
|
||||
try:
|
||||
notional = float(entry_price) * quantity
|
||||
trade_id = Trade.create(
|
||||
symbol=symbol,
|
||||
side=side,
|
||||
quantity=quantity,
|
||||
entry_price=entry_price,
|
||||
leverage=leverage,
|
||||
entry_reason="sync_recovered",
|
||||
entry_order_id=None,
|
||||
notional_usdt=notional,
|
||||
margin_usdt=(notional / leverage) if leverage else None,
|
||||
account_id=self.account_id,
|
||||
stop_loss_price=stop_loss_price,
|
||||
take_profit_price=take_profit_price,
|
||||
)
|
||||
position_info['tradeId'] = trade_id
|
||||
except Exception as db_e:
|
||||
logger.debug(f"{symbol} 补建 DB 记录失败(不影响监控): {db_e}")
|
||||
self.active_positions[symbol] = position_info
|
||||
logger.info(f"{symbol} 已创建临时持仓记录用于监控")
|
||||
logger.info(f"{symbol} 已创建持仓记录用于监控" + (" (已写入 DB)" if position_info.get("tradeId") else ""))
|
||||
# 也为“现有持仓”补挂交易所保护单(重启/掉线更安全)
|
||||
try:
|
||||
mp = None
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user