xmaker: add EnableArbitrage option and makerBook

This commit is contained in:
c9s 2024-09-07 13:47:21 +08:00 committed by lychiyu
parent 47480602fd
commit bb525c72f7

View File

@ -127,6 +127,8 @@ type Strategy struct {
NotifyTrade bool `json:"notifyTrade"` NotifyTrade bool `json:"notifyTrade"`
EnableArbitrage bool `json:"arbitrage"`
// RecoverTrade tries to find the missing trades via the REStful API // RecoverTrade tries to find the missing trades via the REStful API
RecoverTrade bool `json:"recoverTrade"` RecoverTrade bool `json:"recoverTrade"`
@ -160,8 +162,8 @@ type Strategy struct {
ProfitStats *ProfitStats `json:"profitStats,omitempty" persistence:"profit_stats"` ProfitStats *ProfitStats `json:"profitStats,omitempty" persistence:"profit_stats"`
CoveredPosition fixedpoint.Value `json:"coveredPosition,omitempty" persistence:"covered_position"` CoveredPosition fixedpoint.Value `json:"coveredPosition,omitempty" persistence:"covered_position"`
sourceBook *types.StreamOrderBook sourceBook, makerBook *types.StreamOrderBook
activeMakerOrders *bbgo.ActiveOrderBook activeMakerOrders *bbgo.ActiveOrderBook
hedgeErrorLimiter *rate.Limiter hedgeErrorLimiter *rate.Limiter
hedgeErrorRateReservation *rate.Reservation hedgeErrorRateReservation *rate.Reservation
@ -213,6 +215,12 @@ func (s *Strategy) CrossSubscribe(sessions map[string]*bbgo.ExchangeSession) {
makerSession.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{Interval: "1m"}) makerSession.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{Interval: "1m"})
if s.EnableArbitrage {
makerSession.Subscribe(types.BookChannel, s.Symbol, types.SubscribeOptions{
Depth: types.DepthLevelMedium,
})
}
for _, sig := range s.SignalConfigList { for _, sig := range s.SignalConfigList {
if sig.TradeVolumeWindowSignal != nil { if sig.TradeVolumeWindowSignal != nil {
sourceSession.Subscribe(types.MarketTradeChannel, s.Symbol, types.SubscribeOptions{}) sourceSession.Subscribe(types.MarketTradeChannel, s.Symbol, types.SubscribeOptions{})
@ -277,7 +285,7 @@ func (s *Strategy) getBollingerTrend(quote *Quote) int {
} }
func (s *Strategy) applySignalMargin(ctx context.Context, quote *Quote) error { func (s *Strategy) applySignalMargin(ctx context.Context, quote *Quote) error {
signal, err := s.calculateSignal(ctx) signal, err := s.aggregateSignal(ctx)
if err != nil { if err != nil {
return err return err
} }
@ -373,7 +381,7 @@ func (s *Strategy) applyBollingerMargin(
return nil return nil
} }
func (s *Strategy) calculateSignal(ctx context.Context) (float64, error) { func (s *Strategy) aggregateSignal(ctx context.Context) (float64, error) {
sum := 0.0 sum := 0.0
voters := 0.0 voters := 0.0
for _, signal := range s.SignalConfigList { for _, signal := range s.SignalConfigList {
@ -417,10 +425,11 @@ func (s *Strategy) updateQuote(ctx context.Context) {
} }
if s.activeMakerOrders.NumOfOrders() > 0 { if s.activeMakerOrders.NumOfOrders() > 0 {
s.logger.Warnf("unable to cancel all %s orders, skipping placing maker orders", s.Symbol)
return return
} }
signal, err := s.calculateSignal(ctx) signal, err := s.aggregateSignal(ctx)
if err != nil { if err != nil {
return return
} }
@ -447,6 +456,34 @@ func (s *Strategy) updateQuote(ctx context.Context) {
return return
} }
bestBidPrice := bestBid.Price
bestAskPrice := bestAsk.Price
s.logger.Infof("%s book ticker: best ask / best bid = %v / %v", s.Symbol, bestAskPrice, bestBidPrice)
if bestBidPrice.Compare(bestAskPrice) > 0 {
log.Errorf("best bid price %f is higher than best ask price %f, skip quoting",
bestBidPrice.Float64(),
bestAskPrice.Float64(),
)
return
}
if s.EnableArbitrage {
if makerBid, makerAsk, ok := s.makerBook.BestBidAndAsk(); ok {
if makerAsk.Price.Compare(bestBid.Price) <= 0 {
askPvs := s.makerBook.SideBook(types.SideTypeSell)
for _, pv := range askPvs {
if pv.Price.Compare(bestBid.Price) <= 0 {
}
}
// send ioc order for arbitrage
} else if makerBid.Price.Compare(bestAsk.Price) >= 0 {
// send ioc order for arbitrage
}
}
}
// use mid-price for the last price // use mid-price for the last price
s.lastPrice = bestBid.Price.Add(bestAsk.Price).Div(two) s.lastPrice = bestBid.Price.Add(bestAsk.Price).Div(two)
@ -645,18 +682,6 @@ func (s *Strategy) updateQuote(ctx context.Context) {
return return
} }
bestBidPrice := bestBid.Price
bestAskPrice := bestAsk.Price
s.logger.Infof("%s book ticker: best ask / best bid = %v / %v", s.Symbol, bestAskPrice, bestBidPrice)
if bestBidPrice.Compare(bestAskPrice) > 0 {
log.Errorf("best bid price %f is higher than best ask price %f, skip quoting",
bestBidPrice.Float64(),
bestAskPrice.Float64(),
)
return
}
var submitOrders []types.SubmitOrder var submitOrders []types.SubmitOrder
var accumulativeBidQuantity, accumulativeAskQuantity fixedpoint.Value var accumulativeBidQuantity, accumulativeAskQuantity fixedpoint.Value
var bidQuantity = s.Quantity var bidQuantity = s.Quantity
@ -687,14 +712,6 @@ func (s *Strategy) updateQuote(ctx context.Context) {
bidPrice := quote.BestBidPrice bidPrice := quote.BestBidPrice
askPrice := quote.BestAskPrice askPrice := quote.BestAskPrice
if bidPrice.Compare(askPrice) > 0 {
log.Errorf("maker bid price %f is higher than maker ask price %f, skip quoting",
bidPrice.Float64(),
askPrice.Float64(),
)
return
}
bidMarginMetrics.With(s.metricsLabels).Set(quote.BidMargin.Float64()) bidMarginMetrics.With(s.metricsLabels).Set(quote.BidMargin.Float64())
askMarginMetrics.With(s.metricsLabels).Set(quote.AskMargin.Float64()) askMarginMetrics.With(s.metricsLabels).Set(quote.AskMargin.Float64())
@ -1360,6 +1377,9 @@ func (s *Strategy) CrossRun(
s.ProfitStats.ProfitStats = profitStats s.ProfitStats.ProfitStats = profitStats
} }
s.makerBook = types.NewStreamBook(s.Symbol, s.makerSession.ExchangeName)
s.makerBook.BindStream(s.makerSession.MarketDataStream)
s.sourceBook = types.NewStreamBook(s.Symbol, s.sourceSession.ExchangeName) s.sourceBook = types.NewStreamBook(s.Symbol, s.sourceSession.ExchangeName)
s.sourceBook.BindStream(s.sourceSession.MarketDataStream) s.sourceBook.BindStream(s.sourceSession.MarketDataStream)