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