update
This commit is contained in:
parent
ffca7037ed
commit
c464ae6e4d
@ -117,6 +117,7 @@ class Settings(BaseSettings):
|
|||||||
paper_trading_margin_per_order: float = 1000 # 每单保证金 (USDT)
|
paper_trading_margin_per_order: float = 1000 # 每单保证金 (USDT)
|
||||||
paper_trading_max_orders: int = 10 # 最大持仓+挂单总数
|
paper_trading_max_orders: int = 10 # 最大持仓+挂单总数
|
||||||
paper_trading_auto_close_opposite: bool = False # 是否自动平掉反向持仓(智能策略)
|
paper_trading_auto_close_opposite: bool = False # 是否自动平掉反向持仓(智能策略)
|
||||||
|
paper_trading_breakeven_threshold: float = 1 # 保本止损触发阈值(盈利百分比),0表示禁用
|
||||||
# 废弃的配置(保留兼容性)
|
# 废弃的配置(保留兼容性)
|
||||||
paper_trading_position_a: float = 1000 # A级信号仓位 (USDT)
|
paper_trading_position_a: float = 1000 # A级信号仓位 (USDT)
|
||||||
paper_trading_position_b: float = 500 # B级信号仓位 (USDT)
|
paper_trading_position_b: float = 500 # B级信号仓位 (USDT)
|
||||||
|
|||||||
@ -65,6 +65,8 @@ class CryptoAgent:
|
|||||||
event_type = result.get('event_type', 'order_closed')
|
event_type = result.get('event_type', 'order_closed')
|
||||||
if event_type == 'order_filled':
|
if event_type == 'order_filled':
|
||||||
asyncio.run_coroutine_threadsafe(self._notify_order_filled(result), self._event_loop)
|
asyncio.run_coroutine_threadsafe(self._notify_order_filled(result), self._event_loop)
|
||||||
|
elif event_type == 'breakeven_triggered':
|
||||||
|
asyncio.run_coroutine_threadsafe(self._notify_breakeven_triggered(result), self._event_loop)
|
||||||
else:
|
else:
|
||||||
asyncio.run_coroutine_threadsafe(self._notify_order_closed(result), self._event_loop)
|
asyncio.run_coroutine_threadsafe(self._notify_order_closed(result), self._event_loop)
|
||||||
else:
|
else:
|
||||||
@ -106,6 +108,24 @@ class CryptoAgent:
|
|||||||
await self.telegram.send_message(message)
|
await self.telegram.send_message(message)
|
||||||
logger.info(f"已发送挂单撤销通知: {result.get('order_id')}")
|
logger.info(f"已发送挂单撤销通知: {result.get('order_id')}")
|
||||||
|
|
||||||
|
async def _notify_breakeven_triggered(self, result: Dict[str, Any]):
|
||||||
|
"""发送保本止损触发通知"""
|
||||||
|
side_text = "做多" if result.get('side') == 'long' else "做空"
|
||||||
|
|
||||||
|
message = f"""🛡️ 保本止损已启动
|
||||||
|
|
||||||
|
交易对: {result.get('symbol')}
|
||||||
|
方向: {side_text}
|
||||||
|
开仓价: ${result.get('filled_price', 0):,.2f}
|
||||||
|
当前盈利: {result.get('current_pnl_percent', 0):.2f}%
|
||||||
|
止损已移至: ${result.get('new_stop_loss', 0):,.2f}
|
||||||
|
|
||||||
|
✅ 本单已锁定保本,不会亏损"""
|
||||||
|
|
||||||
|
await self.feishu.send_text(message)
|
||||||
|
await self.telegram.send_message(message)
|
||||||
|
logger.info(f"已发送保本止损通知: {result.get('order_id')}")
|
||||||
|
|
||||||
async def _notify_order_closed(self, result: Dict[str, Any]):
|
async def _notify_order_closed(self, result: Dict[str, Any]):
|
||||||
"""发送订单平仓通知"""
|
"""发送订单平仓通知"""
|
||||||
status = result.get('status', '')
|
status = result.get('status', '')
|
||||||
|
|||||||
@ -25,6 +25,7 @@ class PaperTradingService:
|
|||||||
self.margin_per_order = self.settings.paper_trading_margin_per_order # 每单保证金
|
self.margin_per_order = self.settings.paper_trading_margin_per_order # 每单保证金
|
||||||
self.max_orders = self.settings.paper_trading_max_orders # 最大订单数
|
self.max_orders = self.settings.paper_trading_max_orders # 最大订单数
|
||||||
self.auto_close_opposite = self.settings.paper_trading_auto_close_opposite # 是否自动平掉反向持仓
|
self.auto_close_opposite = self.settings.paper_trading_auto_close_opposite # 是否自动平掉反向持仓
|
||||||
|
self.breakeven_threshold = self.settings.paper_trading_breakeven_threshold # 保本止损触发阈值
|
||||||
|
|
||||||
# 确保表已创建
|
# 确保表已创建
|
||||||
self._ensure_table_exists()
|
self._ensure_table_exists()
|
||||||
@ -32,7 +33,7 @@ class PaperTradingService:
|
|||||||
# 加载活跃订单到内存
|
# 加载活跃订单到内存
|
||||||
self._load_active_orders()
|
self._load_active_orders()
|
||||||
|
|
||||||
logger.info(f"模拟交易服务初始化完成(自动平反向持仓: {'启用' if self.auto_close_opposite else '禁用'})")
|
logger.info(f"模拟交易服务初始化完成(自动平反向持仓: {'启用' if self.auto_close_opposite else '禁用'},保本止损阈值: {self.breakeven_threshold}%)")
|
||||||
|
|
||||||
def _ensure_table_exists(self):
|
def _ensure_table_exists(self):
|
||||||
"""确保数据表已创建"""
|
"""确保数据表已创建"""
|
||||||
@ -231,8 +232,10 @@ class PaperTradingService:
|
|||||||
if result:
|
if result:
|
||||||
triggered.append(result)
|
triggered.append(result)
|
||||||
else:
|
else:
|
||||||
# 更新最大回撤和最大盈利
|
# 更新最大回撤和最大盈利,并检查保本止损
|
||||||
self._update_order_extremes(order, current_price)
|
breakeven_result = self._update_order_extremes(order, current_price)
|
||||||
|
if breakeven_result:
|
||||||
|
triggered.append(breakeven_result)
|
||||||
|
|
||||||
return triggered
|
return triggered
|
||||||
|
|
||||||
@ -413,8 +416,8 @@ class PaperTradingService:
|
|||||||
finally:
|
finally:
|
||||||
db.close()
|
db.close()
|
||||||
|
|
||||||
def _update_order_extremes(self, order: PaperOrder, current_price: float):
|
def _update_order_extremes(self, order: PaperOrder, current_price: float) -> Optional[Dict[str, Any]]:
|
||||||
"""更新订单的最大回撤和最大盈利"""
|
"""更新订单的最大回撤和最大盈利,并检查是否需要移动止损到保本"""
|
||||||
if order.side == OrderSide.LONG:
|
if order.side == OrderSide.LONG:
|
||||||
current_pnl_percent = ((current_price - order.filled_price) / order.filled_price) * 100
|
current_pnl_percent = ((current_price - order.filled_price) / order.filled_price) * 100
|
||||||
else:
|
else:
|
||||||
@ -422,13 +425,34 @@ class PaperTradingService:
|
|||||||
|
|
||||||
# 检查是否需要更新极值
|
# 检查是否需要更新极值
|
||||||
needs_update = False
|
needs_update = False
|
||||||
|
breakeven_triggered = False
|
||||||
|
|
||||||
if current_pnl_percent > order.max_profit:
|
if current_pnl_percent > order.max_profit:
|
||||||
order.max_profit = current_pnl_percent
|
order.max_profit = current_pnl_percent
|
||||||
needs_update = True
|
needs_update = True
|
||||||
|
|
||||||
if current_pnl_percent < order.max_drawdown:
|
if current_pnl_percent < order.max_drawdown:
|
||||||
order.max_drawdown = current_pnl_percent
|
order.max_drawdown = current_pnl_percent
|
||||||
needs_update = True
|
needs_update = True
|
||||||
|
|
||||||
|
# 保本止损逻辑:当盈利达到阈值时,将止损移动到开仓价
|
||||||
|
if self.breakeven_threshold > 0 and current_pnl_percent >= self.breakeven_threshold:
|
||||||
|
# 检查止损是否还没有移动到保本位
|
||||||
|
if order.side == OrderSide.LONG:
|
||||||
|
# 做多:止损应该低于开仓价,如果止损还在开仓价下方,则移动到开仓价
|
||||||
|
if order.stop_loss < order.filled_price:
|
||||||
|
order.stop_loss = order.filled_price
|
||||||
|
needs_update = True
|
||||||
|
breakeven_triggered = True
|
||||||
|
logger.info(f"保本止损触发: {order.order_id} | {order.symbol} | 盈利 {current_pnl_percent:.2f}% >= {self.breakeven_threshold}% | 止损移至开仓价 ${order.filled_price:,.2f}")
|
||||||
|
else:
|
||||||
|
# 做空:止损应该高于开仓价,如果止损还在开仓价上方,则移动到开仓价
|
||||||
|
if order.stop_loss > order.filled_price:
|
||||||
|
order.stop_loss = order.filled_price
|
||||||
|
needs_update = True
|
||||||
|
breakeven_triggered = True
|
||||||
|
logger.info(f"保本止损触发: {order.order_id} | {order.symbol} | 盈利 {current_pnl_percent:.2f}% >= {self.breakeven_threshold}% | 止损移至开仓价 ${order.filled_price:,.2f}")
|
||||||
|
|
||||||
# 如果有更新,持久化到数据库
|
# 如果有更新,持久化到数据库
|
||||||
if needs_update:
|
if needs_update:
|
||||||
db = db_service.get_session()
|
db = db_service.get_session()
|
||||||
@ -437,6 +461,7 @@ class PaperTradingService:
|
|||||||
if db_order:
|
if db_order:
|
||||||
db_order.max_profit = order.max_profit
|
db_order.max_profit = order.max_profit
|
||||||
db_order.max_drawdown = order.max_drawdown
|
db_order.max_drawdown = order.max_drawdown
|
||||||
|
db_order.stop_loss = order.stop_loss
|
||||||
db.commit()
|
db.commit()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"更新订单极值失败: {e}")
|
logger.error(f"更新订单极值失败: {e}")
|
||||||
@ -444,6 +469,20 @@ class PaperTradingService:
|
|||||||
finally:
|
finally:
|
||||||
db.close()
|
db.close()
|
||||||
|
|
||||||
|
# 如果触发了保本止损,返回通知信息
|
||||||
|
if breakeven_triggered:
|
||||||
|
return {
|
||||||
|
'event_type': 'breakeven_triggered',
|
||||||
|
'order_id': order.order_id,
|
||||||
|
'symbol': order.symbol,
|
||||||
|
'side': order.side.value,
|
||||||
|
'filled_price': order.filled_price,
|
||||||
|
'new_stop_loss': order.stop_loss,
|
||||||
|
'current_pnl_percent': current_pnl_percent
|
||||||
|
}
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
def close_order_manual(self, order_id: str, exit_price: float) -> Optional[Dict[str, Any]]:
|
def close_order_manual(self, order_id: str, exit_price: float) -> Optional[Dict[str, Any]]:
|
||||||
"""手动平仓或取消挂单"""
|
"""手动平仓或取消挂单"""
|
||||||
if order_id not in self.active_orders:
|
if order_id not in self.active_orders:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user