mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-10 09:11:55 +00:00
Merge pull request #1327 from c9s/narumi/fix-position-risk
FIX: Fix duplicate orders caused by position risk control
This commit is contained in:
commit
2f65793522
|
@ -1,10 +1,27 @@
|
|||
---
|
||||
backtest:
|
||||
startTime: "2023-01-01"
|
||||
endTime: "2023-05-31"
|
||||
symbols:
|
||||
- USDCUSDT
|
||||
sessions:
|
||||
- max
|
||||
accounts:
|
||||
max:
|
||||
balances:
|
||||
USDC: 500.0
|
||||
USDT: 500.0
|
||||
|
||||
exchangeStrategies:
|
||||
- on: max
|
||||
fixedmaker:
|
||||
symbol: BTCUSDT
|
||||
symbol: USDCUSDT
|
||||
interval: 5m
|
||||
halfSpread: 0.05%
|
||||
quantity: 0.005
|
||||
quantity: 15
|
||||
orderType: LIMIT_MAKER
|
||||
dryRun: true
|
||||
dryRun: false
|
||||
|
||||
positionHardLimit: 1500
|
||||
maxPositionQuantity: 1500
|
||||
circuitBreakLossThreshold: -0.15
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -63,7 +63,7 @@ func (s *Strategy) Validate() error {
|
|||
}
|
||||
|
||||
if s.HalfSpread.Float64() <= 0 {
|
||||
return fmt.Errorf("halfSpreadRatio should be positive")
|
||||
return fmt.Errorf("halfSpread should be positive")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user