Fix duplicate orders caused by position risk control

This commit is contained in:
narumi 2023-09-27 11:45:31 +08:00
parent a13c65ef1d
commit d8ff42d531
2 changed files with 37 additions and 20 deletions

View File

@ -24,6 +24,11 @@ type PositionRiskControl struct {
// only used in the ModifiedQuantity method
sliceQuantity fixedpoint.Value
// activeOrderBook is used to store orders created by the risk control.
// This allows us to cancel them before submitting the position release
// orders, preventing duplicate orders.
activeOrderBook *bbgo.ActiveOrderBook
releasePositionCallbacks []func(quantity fixedpoint.Value, side types.SideType)
}
@ -34,26 +39,6 @@ func NewPositionRiskControl(orderExecutor bbgo.OrderExecutorExtended, hardLimit,
sliceQuantity: quantity,
}
control.OnReleasePosition(func(quantity fixedpoint.Value, side types.SideType) {
pos := orderExecutor.Position()
submitOrder := types.SubmitOrder{
Symbol: pos.Symbol,
Market: pos.Market,
Side: side,
Type: types.OrderTypeMarket,
Quantity: quantity,
}
log.Infof("RiskControl: position limit exceeded, submitting order to reduce position: %+v", submitOrder)
createdOrders, err := orderExecutor.SubmitOrders(context.Background(), submitOrder)
if err != nil {
log.WithError(err).Errorf("failed to submit orders")
return
}
log.Infof("created position release orders: %+v", createdOrders)
})
// register position update handler: check if position is over the hard limit
orderExecutor.TradeCollector().OnPositionUpdate(func(position *types.Position) {
if fixedpoint.Compare(position.Base, hardLimit) > 0 {
@ -68,6 +53,37 @@ func NewPositionRiskControl(orderExecutor bbgo.OrderExecutorExtended, hardLimit,
return control
}
func (p *PositionRiskControl) Initialize(ctx context.Context, session *bbgo.ExchangeSession) {
p.activeOrderBook = bbgo.NewActiveOrderBook("")
p.activeOrderBook.BindStream(session.UserDataStream)
p.OnReleasePosition(func(quantity fixedpoint.Value, side types.SideType) {
if err := p.activeOrderBook.GracefulCancel(ctx, session.Exchange); err != nil {
log.WithError(err).Errorf("failed to cancel orders")
}
pos := p.orderExecutor.Position()
submitOrder := types.SubmitOrder{
Symbol: pos.Symbol,
Market: pos.Market,
Side: side,
Type: types.OrderTypeMarket,
Quantity: quantity,
}
log.Infof("RiskControl: position limit exceeded, submitting order to reduce position: %+v", submitOrder)
createdOrders, err := p.orderExecutor.SubmitOrders(ctx, submitOrder)
if err != nil {
log.WithError(err).Errorf("failed to submit orders")
return
}
log.Infof("created position release orders: %+v", createdOrders)
p.activeOrderBook.Add(createdOrders...)
})
}
// ModifiedQuantity returns sliceQuantity controlled by position risks
// For buy orders, modify sliceQuantity = min(hardLimit - position, sliceQuantity), limiting by positive position
// For sell orders, modify sliceQuantity = min(hardLimit - (-position), sliceQuantity), limiting by negative position

View File

@ -76,6 +76,7 @@ func (s *Strategy) Initialize(ctx context.Context, environ *bbgo.Environment, se
if !s.PositionHardLimit.IsZero() && !s.MaxPositionQuantity.IsZero() {
log.Infof("positionHardLimit and maxPositionQuantity are configured, setting up PositionRiskControl...")
s.positionRiskControl = riskcontrol.NewPositionRiskControl(s.OrderExecutor, s.PositionHardLimit, s.MaxPositionQuantity)
s.positionRiskControl.Initialize(ctx, session)
}
if !s.CircuitBreakLossThreshold.IsZero() {