1
This commit is contained in:
parent
569747d8fd
commit
51565a4edf
@ -13,6 +13,7 @@ class OrderStatus(str, Enum):
|
|||||||
OPEN = "open" # 持仓中
|
OPEN = "open" # 持仓中
|
||||||
CLOSED_TP = "closed_tp" # 止盈平仓
|
CLOSED_TP = "closed_tp" # 止盈平仓
|
||||||
CLOSED_SL = "closed_sl" # 止损平仓
|
CLOSED_SL = "closed_sl" # 止损平仓
|
||||||
|
CLOSED_BE = "closed_be" # 保本止损平仓
|
||||||
CLOSED_MANUAL = "closed_manual" # 手动平仓
|
CLOSED_MANUAL = "closed_manual" # 手动平仓
|
||||||
CANCELLED = "cancelled" # 已取消
|
CANCELLED = "cancelled" # 已取消
|
||||||
|
|
||||||
@ -77,6 +78,7 @@ class PaperOrder(Base):
|
|||||||
# 风险指标
|
# 风险指标
|
||||||
max_drawdown = Column(Float, default=0) # 持仓期间最大回撤
|
max_drawdown = Column(Float, default=0) # 持仓期间最大回撤
|
||||||
max_profit = Column(Float, default=0) # 持仓期间最大盈利
|
max_profit = Column(Float, default=0) # 持仓期间最大盈利
|
||||||
|
breakeven_triggered = Column(Integer, default=0) # 是否触发过保本止损(0=否,1=是)
|
||||||
|
|
||||||
# 时间戳
|
# 时间戳
|
||||||
created_at = Column(DateTime, default=datetime.utcnow)
|
created_at = Column(DateTime, default=datetime.utcnow)
|
||||||
@ -111,6 +113,7 @@ class PaperOrder(Base):
|
|||||||
'pnl_percent': self.pnl_percent,
|
'pnl_percent': self.pnl_percent,
|
||||||
'max_drawdown': self.max_drawdown,
|
'max_drawdown': self.max_drawdown,
|
||||||
'max_profit': self.max_profit,
|
'max_profit': self.max_profit,
|
||||||
|
'breakeven_triggered': self.breakeven_triggered,
|
||||||
'created_at': self.created_at.isoformat() if self.created_at else None,
|
'created_at': self.created_at.isoformat() if self.created_at else None,
|
||||||
'opened_at': self.opened_at.isoformat() if self.opened_at else None,
|
'opened_at': self.opened_at.isoformat() if self.opened_at else None,
|
||||||
'closed_at': self.closed_at.isoformat() if self.closed_at else None,
|
'closed_at': self.closed_at.isoformat() if self.closed_at else None,
|
||||||
|
|||||||
@ -431,6 +431,10 @@ class PaperTradingService:
|
|||||||
exit_price = order.take_profit
|
exit_price = order.take_profit
|
||||||
elif current_price <= order.stop_loss:
|
elif current_price <= order.stop_loss:
|
||||||
triggered = True
|
triggered = True
|
||||||
|
# 通过标记判断是否是保本止损
|
||||||
|
if getattr(order, 'breakeven_triggered', 0) == 1:
|
||||||
|
new_status = OrderStatus.CLOSED_BE
|
||||||
|
else:
|
||||||
new_status = OrderStatus.CLOSED_SL
|
new_status = OrderStatus.CLOSED_SL
|
||||||
exit_price = order.stop_loss
|
exit_price = order.stop_loss
|
||||||
else:
|
else:
|
||||||
@ -441,6 +445,10 @@ class PaperTradingService:
|
|||||||
exit_price = order.take_profit
|
exit_price = order.take_profit
|
||||||
elif current_price >= order.stop_loss:
|
elif current_price >= order.stop_loss:
|
||||||
triggered = True
|
triggered = True
|
||||||
|
# 通过标记判断是否是保本止损
|
||||||
|
if getattr(order, 'breakeven_triggered', 0) == 1:
|
||||||
|
new_status = OrderStatus.CLOSED_BE
|
||||||
|
else:
|
||||||
new_status = OrderStatus.CLOSED_SL
|
new_status = OrderStatus.CLOSED_SL
|
||||||
exit_price = order.stop_loss
|
exit_price = order.stop_loss
|
||||||
|
|
||||||
@ -499,7 +507,7 @@ class PaperTradingService:
|
|||||||
'signal_grade': db_order.signal_grade.value if db_order.signal_grade else None
|
'signal_grade': db_order.signal_grade.value if db_order.signal_grade else None
|
||||||
}
|
}
|
||||||
|
|
||||||
status_text = "止盈" if status == OrderStatus.CLOSED_TP else "止损"
|
status_text = {"closed_tp": "止盈", "closed_sl": "止损", "closed_be": "保本止损"}.get(status.value, "平仓")
|
||||||
logger.info(f"订单{status_text}: {db_order.order_id} | {db_order.symbol} | 盈亏: {pnl_percent:+.2f}% (${pnl_amount:+.2f})")
|
logger.info(f"订单{status_text}: {db_order.order_id} | {db_order.symbol} | 盈亏: {pnl_percent:+.2f}% (${pnl_amount:+.2f})")
|
||||||
|
|
||||||
return result
|
return result
|
||||||
@ -534,11 +542,13 @@ class PaperTradingService:
|
|||||||
|
|
||||||
# 保本止损逻辑:当盈利达到阈值时,将止损移动到开仓价
|
# 保本止损逻辑:当盈利达到阈值时,将止损移动到开仓价
|
||||||
if self.breakeven_threshold > 0 and current_pnl_percent >= self.breakeven_threshold:
|
if self.breakeven_threshold > 0 and current_pnl_percent >= self.breakeven_threshold:
|
||||||
# 检查止损是否还没有移动到保本位
|
# 检查止损是否还没有移动到保本位(通过标记判断)
|
||||||
|
if getattr(order, 'breakeven_triggered', 0) != 1:
|
||||||
if order.side == OrderSide.LONG:
|
if order.side == OrderSide.LONG:
|
||||||
# 做多:止损应该低于开仓价,如果止损还在开仓价下方,则移动到开仓价
|
# 做多:止损应该低于开仓价,如果止损还在开仓价下方,则移动到开仓价
|
||||||
if order.stop_loss < order.filled_price:
|
if order.stop_loss < order.filled_price:
|
||||||
order.stop_loss = order.filled_price
|
order.stop_loss = order.filled_price
|
||||||
|
order.breakeven_triggered = 1
|
||||||
needs_update = True
|
needs_update = True
|
||||||
breakeven_triggered = True
|
breakeven_triggered = True
|
||||||
logger.info(f"保本止损触发: {order.order_id} | {order.symbol} | 盈利 {current_pnl_percent:.2f}% >= {self.breakeven_threshold}% | 止损移至开仓价 ${order.filled_price:,.2f}")
|
logger.info(f"保本止损触发: {order.order_id} | {order.symbol} | 盈利 {current_pnl_percent:.2f}% >= {self.breakeven_threshold}% | 止损移至开仓价 ${order.filled_price:,.2f}")
|
||||||
@ -546,6 +556,7 @@ class PaperTradingService:
|
|||||||
# 做空:止损应该高于开仓价,如果止损还在开仓价上方,则移动到开仓价
|
# 做空:止损应该高于开仓价,如果止损还在开仓价上方,则移动到开仓价
|
||||||
if order.stop_loss > order.filled_price:
|
if order.stop_loss > order.filled_price:
|
||||||
order.stop_loss = order.filled_price
|
order.stop_loss = order.filled_price
|
||||||
|
order.breakeven_triggered = 1
|
||||||
needs_update = True
|
needs_update = True
|
||||||
breakeven_triggered = True
|
breakeven_triggered = True
|
||||||
logger.info(f"保本止损触发: {order.order_id} | {order.symbol} | 盈利 {current_pnl_percent:.2f}% >= {self.breakeven_threshold}% | 止损移至开仓价 ${order.filled_price:,.2f}")
|
logger.info(f"保本止损触发: {order.order_id} | {order.symbol} | 盈利 {current_pnl_percent:.2f}% >= {self.breakeven_threshold}% | 止损移至开仓价 ${order.filled_price:,.2f}")
|
||||||
@ -559,6 +570,7 @@ class PaperTradingService:
|
|||||||
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_order.stop_loss = order.stop_loss
|
||||||
|
db_order.breakeven_triggered = order.breakeven_triggered
|
||||||
db.commit()
|
db.commit()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"更新订单极值失败: {e}")
|
logger.error(f"更新订单极值失败: {e}")
|
||||||
@ -779,6 +791,7 @@ class PaperTradingService:
|
|||||||
PaperOrder.status.in_([
|
PaperOrder.status.in_([
|
||||||
OrderStatus.CLOSED_TP,
|
OrderStatus.CLOSED_TP,
|
||||||
OrderStatus.CLOSED_SL,
|
OrderStatus.CLOSED_SL,
|
||||||
|
OrderStatus.CLOSED_BE,
|
||||||
OrderStatus.CLOSED_MANUAL
|
OrderStatus.CLOSED_MANUAL
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
@ -800,6 +813,7 @@ class PaperTradingService:
|
|||||||
PaperOrder.status.in_([
|
PaperOrder.status.in_([
|
||||||
OrderStatus.CLOSED_TP,
|
OrderStatus.CLOSED_TP,
|
||||||
OrderStatus.CLOSED_SL,
|
OrderStatus.CLOSED_SL,
|
||||||
|
OrderStatus.CLOSED_BE,
|
||||||
OrderStatus.CLOSED_MANUAL
|
OrderStatus.CLOSED_MANUAL
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
@ -891,6 +905,7 @@ class PaperTradingService:
|
|||||||
PaperOrder.status.in_([
|
PaperOrder.status.in_([
|
||||||
OrderStatus.CLOSED_TP,
|
OrderStatus.CLOSED_TP,
|
||||||
OrderStatus.CLOSED_SL,
|
OrderStatus.CLOSED_SL,
|
||||||
|
OrderStatus.CLOSED_BE,
|
||||||
OrderStatus.CLOSED_MANUAL
|
OrderStatus.CLOSED_MANUAL
|
||||||
])
|
])
|
||||||
).all()
|
).all()
|
||||||
@ -985,6 +1000,7 @@ class PaperTradingService:
|
|||||||
PaperOrder.status.in_([
|
PaperOrder.status.in_([
|
||||||
OrderStatus.CLOSED_TP,
|
OrderStatus.CLOSED_TP,
|
||||||
OrderStatus.CLOSED_SL,
|
OrderStatus.CLOSED_SL,
|
||||||
|
OrderStatus.CLOSED_BE,
|
||||||
OrderStatus.CLOSED_MANUAL
|
OrderStatus.CLOSED_MANUAL
|
||||||
]),
|
]),
|
||||||
PaperOrder.closed_at >= cutoff_time
|
PaperOrder.closed_at >= cutoff_time
|
||||||
|
|||||||
@ -221,6 +221,11 @@
|
|||||||
color: #ff4444;
|
color: #ff4444;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status-badge.closed_be {
|
||||||
|
background: rgba(255, 193, 7, 0.1);
|
||||||
|
color: #ffc107;
|
||||||
|
}
|
||||||
|
|
||||||
.status-badge.closed_manual {
|
.status-badge.closed_manual {
|
||||||
background: rgba(255, 165, 0, 0.1);
|
background: rgba(255, 165, 0, 0.1);
|
||||||
color: orange;
|
color: orange;
|
||||||
@ -1628,6 +1633,7 @@
|
|||||||
'open': '持仓中',
|
'open': '持仓中',
|
||||||
'closed_tp': '止盈',
|
'closed_tp': '止盈',
|
||||||
'closed_sl': '止损',
|
'closed_sl': '止损',
|
||||||
|
'closed_be': '保本止损',
|
||||||
'closed_manual': '手动平仓'
|
'closed_manual': '手动平仓'
|
||||||
};
|
};
|
||||||
return map[status] || status;
|
return map[status] || status;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user