Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEATURE: [strategy] Add convert strategy #1270

Merged
merged 11 commits into from
Aug 4, 2023
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,15 @@ the implementation.
| grid2 | the second generation grid strategy, it can convert your quote asset into a grid, supports base+quote mode | maker | |
| bollgrid | strategy implements a basic grid strategy with the built-in bollinger indicator | maker | |
| xmaker | cross exchange market making strategy, it hedges your inventory risk on the other side | maker | no |
| xnav | this strategy helps you record the current net asset value | tool | |
| xalign | this strategy aligns your balance position automatically | tool | |
| xfunding | a funding rate fee strategy | funding | |
| xnav | this strategy helps you record the current net asset value | tool | no |
| xalign | this strategy aligns your balance position automatically | tool | no |
| xfunding | a funding rate fee strategy | funding | no |
| autoborrow | this strategy uses margin to borrow assets, to help you keep the minimal balance | tool | no |
| pivotshort | this strategy finds the pivot low and entry the trade when the price breaks the previous low | long/short | |
| schedule | this strategy buy/sell with a fixed quantity periodically, you can use this as a single DCA, or to refill the fee asset like BNB. | tool |
| irr | this strategy opens the position based on the predicated return rate | long/short | |
| bollmaker | this strategy holds a long-term long/short position, places maker orders on both side, uses bollinger band to control the position size | maker | |
| wall | this strategy creates wall (large amount order) on the order book | maker | |
| wall | this strategy creates wall (large amount order) on the order book | maker | no |
| scmaker | this market making strategy is desgiend for stable coin markets, like USDC/USDT | maker | |
| drift | | long/short | |
| rsicross | this strategy opens a long position when the fast rsi cross over the slow rsi, this is a demo strategy for using the v2 indicator | long/short | |
Expand All @@ -119,6 +119,8 @@ the implementation.
| factoryzoo | | long/short | |
| fmaker | | maker | |
| linregmaker | a linear regression based market maker | maker | |
| convert | convert strategy is a tool that helps you convert specific asset to a target asset | tool | no |




Expand Down
26 changes: 26 additions & 0 deletions config/convert.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
exchangeStrategies:
- on: binance
convert:
## the initial asset you want to convert to
from: BNB

## the final asset you want to convert to
to: BTC

## interval is the period of trigger
interval: 1m

## minBalance is the minimal balance line you want to keep
## in this example, it means 1 BNB
minBalance: 1.0

## maxQuantity is the max quantity per order for converting asset
## in this example, it means 2 BNB
maxQuantity: 2.0

## useTakerOrder uses the taker price for the order, so the order will be a taker
## which will be filled immediately
useTakerOrder: true


9 changes: 7 additions & 2 deletions pkg/bbgo/activeorderbook.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,13 @@ func (b *ActiveOrderBook) waitAllClear(ctx context.Context, waitTime, timeout ti
// It calls the exchange cancel order api and then remove the orders from the active orderbook directly.
func (b *ActiveOrderBook) FastCancel(ctx context.Context, ex types.Exchange, orders ...types.Order) error {
// if no orders are given, set to cancelAll
hasSymbol := b.Symbol != ""
if len(orders) == 0 {
orders = b.Orders()
} else {
// simple check on given input
for _, o := range orders {
if b.Symbol != "" && o.Symbol != b.Symbol {
if hasSymbol && o.Symbol != b.Symbol {
return errors.New("[ActiveOrderBook] cancel " + b.Symbol + " orderbook with different order symbol: " + o.Symbol)
}
}
Expand Down Expand Up @@ -157,8 +158,9 @@ func (b *ActiveOrderBook) GracefulCancel(ctx context.Context, ex types.Exchange,
orders = b.Orders()
} else {
// simple check on given input
hasSymbol := b.Symbol != ""
for _, o := range orders {
if b.Symbol != "" && o.Symbol != b.Symbol {
if hasSymbol && o.Symbol != b.Symbol {
return errors.New("[ActiveOrderBook] cancel " + b.Symbol + " orderbook with different symbol: " + o.Symbol)
}
}
Expand Down Expand Up @@ -222,6 +224,7 @@ func (b *ActiveOrderBook) GracefulCancel(ctx context.Context, ex types.Exchange,
}
}
}

orders = leftOrders
}

Expand All @@ -231,6 +234,7 @@ func (b *ActiveOrderBook) GracefulCancel(ctx context.Context, ex types.Exchange,

func (b *ActiveOrderBook) orderUpdateHandler(order types.Order) {
hasSymbol := len(b.Symbol) > 0

if hasSymbol && order.Symbol != b.Symbol {
return
}
Expand Down Expand Up @@ -297,6 +301,7 @@ func (b *ActiveOrderBook) Update(orders ...types.Order) {

func (b *ActiveOrderBook) Add(orders ...types.Order) {
hasSymbol := len(b.Symbol) > 0

for _, order := range orders {
if hasSymbol && b.Symbol != order.Symbol {
continue
Expand Down
45 changes: 31 additions & 14 deletions pkg/bbgo/order_executor_general.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,37 @@ var quantityReduceDelta = fixedpoint.NewFromFloat(0.005)
// This is for the maximum retries
const submitOrderRetryLimit = 5

type BaseOrderExecutor struct {
session *ExchangeSession
activeMakerOrders *ActiveOrderBook
orderStore *core.OrderStore
}

func (e *BaseOrderExecutor) OrderStore() *core.OrderStore {
return e.orderStore
}

func (e *BaseOrderExecutor) ActiveMakerOrders() *ActiveOrderBook {
return e.activeMakerOrders
}

// GracefulCancel cancels all active maker orders if orders are not given, otherwise cancel all the given orders
func (e *BaseOrderExecutor) GracefulCancel(ctx context.Context, orders ...types.Order) error {
if err := e.activeMakerOrders.GracefulCancel(ctx, e.session.Exchange, orders...); err != nil {
return errors.Wrap(err, "graceful cancel error")
}

return nil
}

// GeneralOrderExecutor implements the general order executor for strategy
type GeneralOrderExecutor struct {
session *ExchangeSession
BaseOrderExecutor

symbol string
strategy string
strategyInstanceID string
position *types.Position
activeMakerOrders *ActiveOrderBook
orderStore *core.OrderStore
tradeCollector *core.TradeCollector

logger log.FieldLogger
Expand All @@ -54,13 +76,16 @@ func NewGeneralOrderExecutor(session *ExchangeSession, symbol, strategy, strateg
orderStore := core.NewOrderStore(symbol)

executor := &GeneralOrderExecutor{
session: session,
BaseOrderExecutor: BaseOrderExecutor{
session: session,
activeMakerOrders: NewActiveOrderBook(symbol),
orderStore: orderStore,
},

symbol: symbol,
strategy: strategy,
strategyInstanceID: strategyInstanceID,
position: position,
activeMakerOrders: NewActiveOrderBook(symbol),
orderStore: orderStore,
tradeCollector: core.NewTradeCollector(symbol, position, orderStore),
}

Expand Down Expand Up @@ -123,14 +148,6 @@ func (e *GeneralOrderExecutor) marginAssetMaxBorrowableUpdater(ctx context.Conte
}
}

func (e *GeneralOrderExecutor) OrderStore() *core.OrderStore {
return e.orderStore
}

func (e *GeneralOrderExecutor) ActiveMakerOrders() *ActiveOrderBook {
return e.activeMakerOrders
}

func (e *GeneralOrderExecutor) BindEnvironment(environ *Environment) {
e.tradeCollector.OnProfit(func(trade types.Trade, profit *types.Profit) {
environ.RecordPosition(e.position, trade, profit)
Expand Down
70 changes: 70 additions & 0 deletions pkg/bbgo/order_executor_simple.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package bbgo

import (
"context"

log "github.com/sirupsen/logrus"
"go.uber.org/multierr"

"github.com/c9s/bbgo/pkg/core"
"github.com/c9s/bbgo/pkg/types"
)

// SimpleOrderExecutor implements the minimal order executor
// This order executor does not handle position and profit stats update
type SimpleOrderExecutor struct {
BaseOrderExecutor

logger log.FieldLogger
}

func NewSimpleOrderExecutor(session *ExchangeSession) *SimpleOrderExecutor {
return &SimpleOrderExecutor{
BaseOrderExecutor: BaseOrderExecutor{
session: session,
activeMakerOrders: NewActiveOrderBook(""),
orderStore: core.NewOrderStore(""),
},
}
}

func (e *SimpleOrderExecutor) SubmitOrders(ctx context.Context, submitOrders ...types.SubmitOrder) (types.OrderSlice, error) {
formattedOrders, err := e.session.FormatOrders(submitOrders)
if err != nil {
return nil, err
}

orderCreateCallback := func(createdOrder types.Order) {
e.orderStore.Add(createdOrder)
e.activeMakerOrders.Add(createdOrder)
}

createdOrders, _, err := BatchPlaceOrder(ctx, e.session.Exchange, orderCreateCallback, formattedOrders...)
return createdOrders, err
}

// CancelOrders cancels the given order objects directly
func (e *SimpleOrderExecutor) CancelOrders(ctx context.Context, orders ...types.Order) error {
if len(orders) == 0 {
orders = e.activeMakerOrders.Orders()
}

if len(orders) == 0 {
return nil
}

err := e.session.Exchange.CancelOrders(ctx, orders...)
if err != nil { // Retry once
err2 := e.session.Exchange.CancelOrders(ctx, orders...)
if err2 != nil {
return multierr.Append(err, err2)
}
}

return err
}

func (e *SimpleOrderExecutor) Bind() {
e.activeMakerOrders.BindStream(e.session.UserDataStream)
e.orderStore.BindStream(e.session.UserDataStream)
}
1 change: 1 addition & 0 deletions pkg/cmd/strategy/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
_ "github.com/c9s/bbgo/pkg/strategy/autoborrow"
_ "github.com/c9s/bbgo/pkg/strategy/bollgrid"
_ "github.com/c9s/bbgo/pkg/strategy/bollmaker"
_ "github.com/c9s/bbgo/pkg/strategy/convert"
_ "github.com/c9s/bbgo/pkg/strategy/dca"
_ "github.com/c9s/bbgo/pkg/strategy/drift"
_ "github.com/c9s/bbgo/pkg/strategy/elliottwave"
Expand Down
Loading
Loading