mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-21 22:43:52 +00:00
add universal cancel all orders api helper
This commit is contained in:
parent
945c442b92
commit
3b8a3bed5f
|
@ -23,6 +23,9 @@ func (g *GracefulShutdown) Shutdown(shutdownCtx context.Context) {
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OnShutdown helps you register your shutdown handler
|
||||||
|
// the first context object is where you want to register your shutdown handler, where the context has the isolated storage.
|
||||||
|
// in your handler, you will get another context for the timeout context.
|
||||||
func OnShutdown(ctx context.Context, f ShutdownHandler) {
|
func OnShutdown(ctx context.Context, f ShutdownHandler) {
|
||||||
isolatedContext := GetIsolationFromContext(ctx)
|
isolatedContext := GetIsolationFromContext(ctx)
|
||||||
isolatedContext.gracefulShutdown.OnShutdown(f)
|
isolatedContext.gracefulShutdown.OnShutdown(f)
|
||||||
|
|
|
@ -172,7 +172,6 @@ func toGlobalOrder(maxOrder max.Order) (*types.Order, error) {
|
||||||
executedVolume := maxOrder.ExecutedVolume
|
executedVolume := maxOrder.ExecutedVolume
|
||||||
remainingVolume := maxOrder.RemainingVolume
|
remainingVolume := maxOrder.RemainingVolume
|
||||||
isMargin := maxOrder.WalletType == max.WalletTypeMargin
|
isMargin := maxOrder.WalletType == max.WalletTypeMargin
|
||||||
|
|
||||||
return &types.Order{
|
return &types.Order{
|
||||||
SubmitOrder: types.SubmitOrder{
|
SubmitOrder: types.SubmitOrder{
|
||||||
ClientOrderID: maxOrder.ClientOID,
|
ClientOrderID: maxOrder.ClientOID,
|
||||||
|
|
|
@ -8,15 +8,17 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"go.uber.org/multierr"
|
||||||
|
|
||||||
"github.com/c9s/bbgo/pkg/bbgo"
|
"github.com/c9s/bbgo/pkg/bbgo"
|
||||||
"github.com/c9s/bbgo/pkg/exchange/retry"
|
"github.com/c9s/bbgo/pkg/exchange/retry"
|
||||||
"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/types"
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
"github.com/c9s/bbgo/pkg/util"
|
"github.com/c9s/bbgo/pkg/util"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/c9s/bbgo/pkg/util/tradingutil"
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"go.uber.org/multierr"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const ID = "dca2"
|
const ID = "dca2"
|
||||||
|
@ -289,30 +291,29 @@ func (s *Strategy) CleanUp(ctx context.Context) error {
|
||||||
return fmt.Errorf("Session is nil, please check it")
|
return fmt.Errorf("Session is nil, please check it")
|
||||||
}
|
}
|
||||||
|
|
||||||
service, support := session.Exchange.(advancedOrderCancelApi)
|
// ignore the first cancel error, this skips one open-orders query request
|
||||||
if !support {
|
if err := tradingutil.UniversalCancelAllOrders(ctx, session.Exchange, nil); err == nil {
|
||||||
return fmt.Errorf("advancedOrderCancelApi interface is not implemented, fallback to default graceful cancel, exchange %T", session)
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if cancel all orders returns error, get the open orders and retry the cancel in each round
|
||||||
var werr error
|
var werr error
|
||||||
for {
|
for {
|
||||||
s.logger.Infof("checking %s open orders...", s.Symbol)
|
s.logger.Infof("checking %s open orders...", s.Symbol)
|
||||||
|
|
||||||
openOrders, err := retry.QueryOpenOrdersUntilSuccessful(ctx, session.Exchange, s.Symbol)
|
openOrders, err := retry.QueryOpenOrdersUntilSuccessful(ctx, session.Exchange, s.Symbol)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.WithError(err).Errorf("CancelOrdersByGroupID api call error")
|
s.logger.WithError(err).Errorf("unable to query open orders")
|
||||||
werr = multierr.Append(werr, err)
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// all clean up
|
||||||
if len(openOrders) == 0 {
|
if len(openOrders) == 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
s.logger.Infof("found %d open orders left, using cancel all orders api", len(openOrders))
|
if err := tradingutil.UniversalCancelAllOrders(ctx, session.Exchange, openOrders); err != nil {
|
||||||
|
s.logger.WithError(err).Errorf("unable to cancel all orders")
|
||||||
s.logger.Infof("using cancal all orders api for canceling grid orders...")
|
|
||||||
if err := retry.CancelAllOrdersUntilSuccessful(ctx, service); err != nil {
|
|
||||||
s.logger.WithError(err).Errorf("CancelAllOrders api call error")
|
|
||||||
werr = multierr.Append(werr, err)
|
werr = multierr.Append(werr, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
116
pkg/util/tradingutil/cancel.go
Normal file
116
pkg/util/tradingutil/cancel.go
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
package tradingutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/exchange/retry"
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CancelAllOrdersService interface {
|
||||||
|
CancelAllOrders(ctx context.Context) ([]types.Order, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type CancelAllOrdersBySymbolService interface {
|
||||||
|
CancelOrdersBySymbol(ctx context.Context, symbol string) ([]types.Order, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type CancelAllOrdersByGroupIDService interface {
|
||||||
|
CancelOrdersByGroupID(ctx context.Context, groupID uint32) ([]types.Order, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UniversalCancelAllOrders checks if the exchange instance supports the best order cancel strategy
|
||||||
|
// it tries the first interface CancelAllOrdersService that does not need any existing order information or symbol information.
|
||||||
|
//
|
||||||
|
// if CancelAllOrdersService is not supported, then it tries CancelAllOrdersBySymbolService which needs at least one symbol
|
||||||
|
// for the cancel api request.
|
||||||
|
func UniversalCancelAllOrders(ctx context.Context, exchange types.Exchange, openOrders []types.Order) error {
|
||||||
|
if service, ok := exchange.(CancelAllOrdersService); ok {
|
||||||
|
if _, err := service.CancelAllOrders(ctx); err == nil {
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
log.WithError(err).Errorf("unable to cancel all orders")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(openOrders) == 0 {
|
||||||
|
return errors.New("to cancel all orders, openOrders can not be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if service, ok := exchange.(CancelAllOrdersBySymbolService); ok {
|
||||||
|
var symbols = CollectOrderSymbols(openOrders)
|
||||||
|
var anyErr error
|
||||||
|
for _, symbol := range symbols {
|
||||||
|
_, err := service.CancelOrdersBySymbol(ctx, symbol)
|
||||||
|
if err != nil {
|
||||||
|
anyErr = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if anyErr == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if service, ok := exchange.(CancelAllOrdersByGroupIDService); ok {
|
||||||
|
var groupIds = CollectOrderGroupIds(openOrders)
|
||||||
|
var anyErr error
|
||||||
|
for _, groupId := range groupIds {
|
||||||
|
if _, err := service.CancelOrdersByGroupID(ctx, groupId); err != nil {
|
||||||
|
anyErr = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if anyErr == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("unable to cancel all orders: %+v", openOrders)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CollectOrderGroupIds(orders []types.Order) (groupIds []uint32) {
|
||||||
|
groupIdMap := map[uint32]struct{}{}
|
||||||
|
for _, o := range orders {
|
||||||
|
if o.GroupID > 0 {
|
||||||
|
groupIdMap[o.GroupID] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for id := range groupIdMap {
|
||||||
|
groupIds = append(groupIds, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return groupIds
|
||||||
|
}
|
||||||
|
|
||||||
|
func CollectOrderSymbols(orders []types.Order) (symbols []string) {
|
||||||
|
symbolMap := map[string]struct{}{}
|
||||||
|
for _, o := range orders {
|
||||||
|
symbolMap[o.Symbol] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
for s := range symbolMap {
|
||||||
|
symbols = append(symbols, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return symbols
|
||||||
|
}
|
||||||
|
|
||||||
|
func CollectOpenOrders(ctx context.Context, ex types.Exchange, symbols ...string) ([]types.Order, error) {
|
||||||
|
var collectedOrders []types.Order
|
||||||
|
for _, symbol := range symbols {
|
||||||
|
openOrders, err := retry.QueryOpenOrdersUntilSuccessful(ctx, ex, symbol)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
collectedOrders = append(collectedOrders, openOrders...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return collectedOrders, nil
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user