apply inventory-skew to xfixedmaker

This commit is contained in:
narumi 2023-12-21 16:34:05 +08:00
parent f160ea856f
commit 7f0a4a9953
5 changed files with 51 additions and 25 deletions

View File

@ -6,6 +6,7 @@ sessions:
binance: binance:
exchange: binance exchange: binance
envVarPrefix: binance envVarPrefix: binance
publicOnly: true
backtest: backtest:
startTime: "2023-01-01" startTime: "2023-01-01"
@ -25,11 +26,11 @@ crossExchangeStrategies:
- xfixedmaker: - xfixedmaker:
tradingExchange: max tradingExchange: max
symbol: BTCUSDT symbol: BTCUSDT
interval: 5m interval: 1m
halfSpread: 0.01% halfSpread: 0.01%
quantity: 0.005 quantity: 0.005
orderType: LIMIT_MAKER orderType: LIMIT_MAKER
dryRun: false dryRun: true
referenceExchange: binance referenceExchange: binance
referencePriceEMA: referencePriceEMA:
@ -44,3 +45,7 @@ crossExchangeStrategies:
circuitBreakEMA: circuitBreakEMA:
interval: 1m interval: 1m
window: 14 window: 14
inventorySkew:
inventoryRangeMultiplier: 1.0
targetBaseRatio: 0.5

View File

@ -12,8 +12,8 @@ var (
) )
type InventorySkewBidAskRatios struct { type InventorySkewBidAskRatios struct {
bidRatio fixedpoint.Value BidRatio fixedpoint.Value
askRatio fixedpoint.Value AskRatio fixedpoint.Value
} }
// https://hummingbot.org/strategy-configs/inventory-skew/ // https://hummingbot.org/strategy-configs/inventory-skew/
@ -46,8 +46,8 @@ func (s *InventorySkew) CalculateBidAskRatios(quantity fixedpoint.Value, price f
askAdjustment := interp(baseValue, leftLimit, rightLimit, zero, two).Clamp(zero, two) askAdjustment := interp(baseValue, leftLimit, rightLimit, zero, two).Clamp(zero, two)
return &InventorySkewBidAskRatios{ return &InventorySkewBidAskRatios{
bidRatio: bidAdjustment, BidRatio: bidAdjustment,
askRatio: askAdjustment, AskRatio: askAdjustment,
} }
} }

View File

@ -21,8 +21,8 @@ func Test_InventorySkew_CalculateBidAskRatios(t *testing.T) {
baseBalance: fixedpoint.NewFromFloat(1.0), baseBalance: fixedpoint.NewFromFloat(1.0),
quoteBalance: fixedpoint.NewFromFloat(1000), quoteBalance: fixedpoint.NewFromFloat(1000),
want: &InventorySkewBidAskRatios{ want: &InventorySkewBidAskRatios{
bidRatio: fixedpoint.NewFromFloat(1.0), BidRatio: fixedpoint.NewFromFloat(1.0),
askRatio: fixedpoint.NewFromFloat(1.0), AskRatio: fixedpoint.NewFromFloat(1.0),
}, },
}, },
{ {
@ -31,8 +31,8 @@ func Test_InventorySkew_CalculateBidAskRatios(t *testing.T) {
baseBalance: fixedpoint.NewFromFloat(1.0), baseBalance: fixedpoint.NewFromFloat(1.0),
quoteBalance: fixedpoint.NewFromFloat(1200), quoteBalance: fixedpoint.NewFromFloat(1200),
want: &InventorySkewBidAskRatios{ want: &InventorySkewBidAskRatios{
bidRatio: fixedpoint.NewFromFloat(1.5), BidRatio: fixedpoint.NewFromFloat(1.5),
askRatio: fixedpoint.NewFromFloat(0.5), AskRatio: fixedpoint.NewFromFloat(0.5),
}, },
}, },
{ {
@ -41,8 +41,8 @@ func Test_InventorySkew_CalculateBidAskRatios(t *testing.T) {
baseBalance: fixedpoint.NewFromFloat(0.0), baseBalance: fixedpoint.NewFromFloat(0.0),
quoteBalance: fixedpoint.NewFromFloat(10000), quoteBalance: fixedpoint.NewFromFloat(10000),
want: &InventorySkewBidAskRatios{ want: &InventorySkewBidAskRatios{
bidRatio: fixedpoint.NewFromFloat(2.0), BidRatio: fixedpoint.NewFromFloat(2.0),
askRatio: fixedpoint.NewFromFloat(0.0), AskRatio: fixedpoint.NewFromFloat(0.0),
}, },
}, },
{ {
@ -51,8 +51,8 @@ func Test_InventorySkew_CalculateBidAskRatios(t *testing.T) {
baseBalance: fixedpoint.NewFromFloat(2.0), baseBalance: fixedpoint.NewFromFloat(2.0),
quoteBalance: fixedpoint.NewFromFloat(0.0), quoteBalance: fixedpoint.NewFromFloat(0.0),
want: &InventorySkewBidAskRatios{ want: &InventorySkewBidAskRatios{
bidRatio: fixedpoint.NewFromFloat(0.0), BidRatio: fixedpoint.NewFromFloat(0.0),
askRatio: fixedpoint.NewFromFloat(2.0), AskRatio: fixedpoint.NewFromFloat(2.0),
}, },
}, },
} }
@ -63,7 +63,7 @@ func Test_InventorySkew_CalculateBidAskRatios(t *testing.T) {
TargetBaseRatio: fixedpoint.NewFromFloat(0.5), TargetBaseRatio: fixedpoint.NewFromFloat(0.5),
} }
got := s.CalculateBidAskRatios(c.quantity, c.price, c.baseBalance, c.quoteBalance) got := s.CalculateBidAskRatios(c.quantity, c.price, c.baseBalance, c.quoteBalance)
assert.Equal(t, c.want.bidRatio.Float64(), got.bidRatio.Float64()) assert.Equal(t, c.want.BidRatio.Float64(), got.BidRatio.Float64())
assert.Equal(t, c.want.askRatio.Float64(), got.askRatio.Float64()) assert.Equal(t, c.want.AskRatio.Float64(), got.AskRatio.Float64())
} }
} }

View File

@ -195,9 +195,9 @@ func (s *Strategy) generateOrders(ctx context.Context) ([]types.SubmitOrder, err
baseBalance.Total(), baseBalance.Total(),
quoteBalance.Total(), quoteBalance.Total(),
) )
log.Infof("bid ratio: %s, ask ratio: %s", ratios.bidRatio.String(), ratios.askRatio.String()) log.Infof("bid ratio: %s, ask ratio: %s", ratios.BidRatio.String(), ratios.AskRatio.String())
buyQuantity = s.Quantity.Mul(ratios.bidRatio) buyQuantity = s.Quantity.Mul(ratios.BidRatio)
sellQuantity = s.Quantity.Mul(ratios.askRatio) sellQuantity = s.Quantity.Mul(ratios.AskRatio)
log.Infof("buy quantity: %s, sell quantity: %s", buyQuantity.String(), sellQuantity.String()) log.Infof("buy quantity: %s, sell quantity: %s", buyQuantity.String(), sellQuantity.String())
} }

View File

@ -10,6 +10,7 @@ import (
"github.com/c9s/bbgo/pkg/bbgo" "github.com/c9s/bbgo/pkg/bbgo"
"github.com/c9s/bbgo/pkg/fixedpoint" "github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/strategy/common" "github.com/c9s/bbgo/pkg/strategy/common"
"github.com/c9s/bbgo/pkg/strategy/fixedmaker"
"github.com/c9s/bbgo/pkg/types" "github.com/c9s/bbgo/pkg/types"
) )
@ -38,6 +39,7 @@ type Strategy struct {
ReferenceExchange string `json:"referenceExchange"` ReferenceExchange string `json:"referenceExchange"`
ReferencePriceEMA types.IntervalWindow `json:"referencePriceEMA"` ReferencePriceEMA types.IntervalWindow `json:"referencePriceEMA"`
OrderPriceLossThreshold fixedpoint.Value `json:"orderPriceLossThreshold"` OrderPriceLossThreshold fixedpoint.Value `json:"orderPriceLossThreshold"`
InventorySkew fixedmaker.InventorySkew `json:"inventorySkew"`
market types.Market market types.Market
activeOrderBook *bbgo.ActiveOrderBook activeOrderBook *bbgo.ActiveOrderBook
@ -73,6 +75,10 @@ func (s *Strategy) Validate() error {
if s.HalfSpread.Float64() <= 0 { if s.HalfSpread.Float64() <= 0 {
return fmt.Errorf("halfSpread should be positive") return fmt.Errorf("halfSpread should be positive")
} }
if err := s.InventorySkew.Validate(); err != nil {
return err
}
return nil return nil
} }
@ -155,7 +161,7 @@ func (s *Strategy) CrossRun(ctx context.Context, _ bbgo.OrderExecutionRouter, se
} }
func (s *Strategy) cancelOrders(ctx context.Context) { func (s *Strategy) cancelOrders(ctx context.Context) {
if err := s.Session.Exchange.CancelOrders(ctx, s.activeOrderBook.Orders()...); err != nil { if err := s.activeOrderBook.GracefulCancel(ctx, s.Session.Exchange); err != nil {
log.WithError(err).Errorf("failed to cancel orders") log.WithError(err).Errorf("failed to cancel orders")
} }
} }
@ -212,6 +218,21 @@ func (s *Strategy) generateOrders(ctx context.Context) ([]types.SubmitOrder, err
buyPrice := midPrice.Mul(fixedpoint.One.Sub(s.HalfSpread)).Round(s.market.PricePrecision, fixedpoint.Down) buyPrice := midPrice.Mul(fixedpoint.One.Sub(s.HalfSpread)).Round(s.market.PricePrecision, fixedpoint.Down)
log.Infof("sell price: %s, buy price: %s", sellPrice.String(), buyPrice.String()) log.Infof("sell price: %s, buy price: %s", sellPrice.String(), buyPrice.String())
buyQuantity := s.Quantity
sellQuantity := s.Quantity
if !s.InventorySkew.InventoryRangeMultiplier.IsZero() {
ratios := s.InventorySkew.CalculateBidAskRatios(
s.Quantity,
midPrice,
baseBalance.Total(),
quoteBalance.Total(),
)
log.Infof("bid ratio: %s, ask ratio: %s", ratios.BidRatio.String(), ratios.AskRatio.String())
buyQuantity = s.Quantity.Mul(ratios.BidRatio)
sellQuantity = s.Quantity.Mul(ratios.AskRatio)
log.Infof("buy quantity: %s, sell quantity: %s", buyQuantity.String(), sellQuantity.String())
}
// check balance and generate orders // check balance and generate orders
amount := s.Quantity.Mul(buyPrice) amount := s.Quantity.Mul(buyPrice)
if quoteBalance.Available.Compare(amount) > 0 { if quoteBalance.Available.Compare(amount) > 0 {
@ -221,7 +242,7 @@ func (s *Strategy) generateOrders(ctx context.Context) ([]types.SubmitOrder, err
Side: types.SideTypeBuy, Side: types.SideTypeBuy,
Type: s.OrderType, Type: s.OrderType,
Price: buyPrice, Price: buyPrice,
Quantity: s.Quantity, Quantity: buyQuantity,
}) })
} else { } else {
@ -238,7 +259,7 @@ func (s *Strategy) generateOrders(ctx context.Context) ([]types.SubmitOrder, err
Side: types.SideTypeSell, Side: types.SideTypeSell,
Type: s.OrderType, Type: s.OrderType,
Price: sellPrice, Price: sellPrice,
Quantity: s.Quantity, Quantity: sellQuantity,
}) })
} else { } else {
log.Infof("ref price risk control triggered, not placing sell order") log.Infof("ref price risk control triggered, not placing sell order")