mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-25 16:25:16 +00:00
feature: add cancelNoWait in GeneralOrderExecutor to make cancellation in 1s faster
This commit is contained in:
parent
763bb45842
commit
09c85d346c
|
@ -3,9 +3,9 @@ package bbgo
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
|
@ -92,60 +92,49 @@ func (b *ActiveOrderBook) waitAllClear(ctx context.Context, waitTime, timeout ti
|
|||
}
|
||||
}
|
||||
|
||||
// Cancel cancels the given order from activeOrderBook gracefully
|
||||
func (b *ActiveOrderBook) Cancel(ctx context.Context, ex types.Exchange, order types.Order) error {
|
||||
if !b.Exists(order) {
|
||||
return fmt.Errorf("cannot find %v in orderbook", order)
|
||||
// Cancel orders without confirmation
|
||||
func (b *ActiveOrderBook) CancelNoWait(ctx context.Context, ex types.Exchange, orders ...types.Order) error {
|
||||
// if no orders are given, set to cancelAll
|
||||
if len(orders) == 0 {
|
||||
orders = b.Orders()
|
||||
} else {
|
||||
// simple check on given input
|
||||
for _, o := range orders {
|
||||
if o.Symbol != b.Symbol {
|
||||
return errors.New("[ActiveOrderBook] cancel " + b.Symbol + " orderbook with different symbol: " + o.Symbol)
|
||||
}
|
||||
}
|
||||
}
|
||||
// optimize order cancel for back-testing
|
||||
if IsBackTesting {
|
||||
return ex.CancelOrders(context.Background(), order)
|
||||
return ex.CancelOrders(context.Background(), orders...)
|
||||
}
|
||||
log.Debugf("[ActiveOrderBook] gracefully cancelling %v order...", order.OrderID)
|
||||
waitTime := CancelOrderWaitTime
|
||||
|
||||
startTime := time.Now()
|
||||
// ensure order is cancelled
|
||||
for {
|
||||
// Some orders in the variable are not created on the server side yet,
|
||||
// If we cancel these orders directly, we will get an unsent order error
|
||||
// We wait here for a while for server to create these orders.
|
||||
// time.Sleep(SentOrderWaitTime)
|
||||
|
||||
// since ctx might be canceled, we should use background context here
|
||||
|
||||
if err := ex.CancelOrders(context.Background(), order); err != nil {
|
||||
log.WithError(err).Errorf("[ActiveORderBook] can not cancel %v order", order.OrderID)
|
||||
}
|
||||
log.Debugf("[ActiveOrderBook] waiting %s for %v order to be cancelled...", waitTime, order.OrderID)
|
||||
clear, err := b.waitClear(ctx, order, waitTime, 5*time.Second)
|
||||
if clear || err != nil {
|
||||
break
|
||||
}
|
||||
b.Print()
|
||||
|
||||
openOrders, err := ex.QueryOpenOrders(ctx, order.Symbol)
|
||||
if err != nil {
|
||||
log.WithError(err).Errorf("can not query %s open orders", order.Symbol)
|
||||
continue
|
||||
}
|
||||
|
||||
openOrderStore := NewOrderStore(order.Symbol)
|
||||
openOrderStore.Add(openOrders...)
|
||||
// if it's not on the order book (open orders), we should remove it from our local side
|
||||
if !openOrderStore.Exists(order.OrderID) {
|
||||
b.Remove(order)
|
||||
}
|
||||
log.Debugf("[ActiveOrderBook] no wait cancelling %s orders...", b.Symbol)
|
||||
// since ctx might be canceled, we should use background context here
|
||||
if err := ex.CancelOrders(context.Background(), orders...); err != nil {
|
||||
log.WithError(err).Errorf("[ActiveOrderBook] no wait can not cancel %s orders", b.Symbol)
|
||||
}
|
||||
for _, o := range orders {
|
||||
b.Remove(o)
|
||||
}
|
||||
log.Debugf("[ActiveOrderBook] %v(%s) order is cancelled successfully in %s", order.OrderID, b.Symbol, time.Since(startTime))
|
||||
return nil
|
||||
}
|
||||
|
||||
// GracefulCancel cancels the active orders gracefully
|
||||
func (b *ActiveOrderBook) GracefulCancel(ctx context.Context, ex types.Exchange) error {
|
||||
func (b *ActiveOrderBook) GracefulCancel(ctx context.Context, ex types.Exchange, orders ...types.Order) error {
|
||||
// if no orders are given, set to cancelAll
|
||||
if len(orders) == 0 {
|
||||
orders = b.Orders()
|
||||
} else {
|
||||
// simple check on given input
|
||||
for _, o := range orders {
|
||||
if b.Symbol != "" && o.Symbol != b.Symbol {
|
||||
return errors.New("[ActiveOrderBook] cancel " + b.Symbol + " orderbook with different symbol: " + o.Symbol)
|
||||
}
|
||||
}
|
||||
}
|
||||
// optimize order cancel for back-testing
|
||||
if IsBackTesting {
|
||||
orders := b.Orders()
|
||||
return ex.CancelOrders(context.Background(), orders...)
|
||||
}
|
||||
|
||||
|
@ -155,8 +144,6 @@ func (b *ActiveOrderBook) GracefulCancel(ctx context.Context, ex types.Exchange)
|
|||
startTime := time.Now()
|
||||
// ensure every order is cancelled
|
||||
for {
|
||||
orders := b.Orders()
|
||||
|
||||
// Some orders in the variable are not created on the server side yet,
|
||||
// If we cancel these orders directly, we will get an unsent order error
|
||||
// We wait here for a while for server to create these orders.
|
||||
|
@ -180,12 +167,12 @@ func (b *ActiveOrderBook) GracefulCancel(ctx context.Context, ex types.Exchange)
|
|||
// verify the current open orders via the RESTful API
|
||||
log.Warnf("[ActiveOrderBook] using REStful API to verify active orders...")
|
||||
|
||||
orders = b.Orders()
|
||||
var symbols = map[string]struct{}{}
|
||||
for _, order := range orders {
|
||||
symbols[order.Symbol] = struct{}{}
|
||||
|
||||
}
|
||||
var leftOrders []types.Order
|
||||
|
||||
for symbol := range symbols {
|
||||
openOrders, err := ex.QueryOpenOrders(ctx, symbol)
|
||||
|
@ -200,9 +187,12 @@ func (b *ActiveOrderBook) GracefulCancel(ctx context.Context, ex types.Exchange)
|
|||
// if it's not on the order book (open orders), we should remove it from our local side
|
||||
if !openOrderStore.Exists(o.OrderID) {
|
||||
b.Remove(o)
|
||||
} else {
|
||||
leftOrders = append(leftOrders, o)
|
||||
}
|
||||
}
|
||||
}
|
||||
orders = leftOrders
|
||||
}
|
||||
|
||||
log.Debugf("[ActiveOrderBook] all %s orders are cancelled successfully in %s", b.Symbol, time.Since(startTime))
|
||||
|
|
|
@ -356,11 +356,11 @@ func (e *GeneralOrderExecutor) OpenPosition(ctx context.Context, options OpenPos
|
|||
}
|
||||
|
||||
// GracefulCancelActiveOrderBook cancels the orders from the active orderbook.
|
||||
func (e *GeneralOrderExecutor) GracefulCancelActiveOrderBook(ctx context.Context, activeOrders *ActiveOrderBook) error {
|
||||
func (e *GeneralOrderExecutor) GracefulCancelActiveOrderBook(ctx context.Context, activeOrders *ActiveOrderBook, orders ...types.Order) error {
|
||||
if activeOrders.NumOfOrders() == 0 {
|
||||
return nil
|
||||
}
|
||||
if err := activeOrders.GracefulCancel(ctx, e.session.Exchange); err != nil {
|
||||
if err := activeOrders.GracefulCancel(ctx, e.session.Exchange, orders...); err != nil {
|
||||
// Retry once
|
||||
if err = activeOrders.GracefulCancel(ctx, e.session.Exchange); err != nil {
|
||||
return fmt.Errorf("graceful cancel order error: %w", err)
|
||||
|
@ -371,23 +371,25 @@ func (e *GeneralOrderExecutor) GracefulCancelActiveOrderBook(ctx context.Context
|
|||
return nil
|
||||
}
|
||||
|
||||
func (e *GeneralOrderExecutor) GracefulCancelOrder(ctx context.Context, order types.Order) error {
|
||||
if e.activeMakerOrders.NumOfOrders() == 0 {
|
||||
// CancelActiveOrderBookNoWait cancels the orders from the active orderbook without waiting
|
||||
func (e *GeneralOrderExecutor) CancelActiveOrderBookNoWait(ctx context.Context, activeOrders *ActiveOrderBook, orders ...types.Order) error {
|
||||
if activeOrders.NumOfOrders() == 0 {
|
||||
return nil
|
||||
}
|
||||
if err := e.activeMakerOrders.Cancel(ctx, e.session.Exchange, order); err != nil {
|
||||
// Retry once
|
||||
if err = e.activeMakerOrders.Cancel(ctx, e.session.Exchange, order); err != nil {
|
||||
return fmt.Errorf("cancel order error: %w", err)
|
||||
}
|
||||
if err := activeOrders.CancelNoWait(ctx, e.session.Exchange, orders...); err != nil {
|
||||
return fmt.Errorf("cancel order error: %w", err)
|
||||
}
|
||||
e.tradeCollector.Process()
|
||||
return nil
|
||||
}
|
||||
|
||||
// GracefulCancel cancels all active maker orders
|
||||
func (e *GeneralOrderExecutor) GracefulCancel(ctx context.Context) error {
|
||||
return e.GracefulCancelActiveOrderBook(ctx, e.activeMakerOrders)
|
||||
// GracefulCancel cancels all active maker orders if orders are not given, otherwise cancel all the given orders
|
||||
func (e *GeneralOrderExecutor) GracefulCancel(ctx context.Context, orders ...types.Order) error {
|
||||
return e.GracefulCancelActiveOrderBook(ctx, e.activeMakerOrders, orders...)
|
||||
}
|
||||
|
||||
// CancelNoWait cancels all active maker orders if orders is not given, otherwise cancel the given orders
|
||||
func (e *GeneralOrderExecutor) CancelNoWait(ctx context.Context, orders ...types.Order) error {
|
||||
return e.CancelActiveOrderBookNoWait(ctx, e.activeMakerOrders, orders...)
|
||||
}
|
||||
|
||||
// ClosePosition closes the current position by a percentage.
|
||||
|
|
|
@ -202,7 +202,7 @@ func (s *Strategy) smartCancel(ctx context.Context, pricef float64) int {
|
|||
panic("not supported side for the order")
|
||||
}
|
||||
if toCancel {
|
||||
err := s.GeneralOrderExecutor.GracefulCancelOrder(ctx, order)
|
||||
err := s.GeneralOrderExecutor.GracefulCancel(ctx, order)
|
||||
if err == nil {
|
||||
delete(s.orderPendingCounter, order.OrderID)
|
||||
} else {
|
||||
|
|
Loading…
Reference in New Issue
Block a user