From 1d1d5d497f86e1769ac41fa9661f0d9dc020cf20 Mon Sep 17 00:00:00 2001 From: c9s Date: Mon, 19 Sep 2022 09:25:54 +0800 Subject: [PATCH 01/15] bbgo: init call to updateMarginAssetMaxBorrowable --- pkg/bbgo/order_executor_general.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/bbgo/order_executor_general.go b/pkg/bbgo/order_executor_general.go index 0b8f60f67..91eabedbf 100644 --- a/pkg/bbgo/order_executor_general.go +++ b/pkg/bbgo/order_executor_general.go @@ -88,6 +88,7 @@ func (e *GeneralOrderExecutor) marginAssetMaxBorrowableUpdater(ctx context.Conte t := time.NewTicker(util.MillisecondsJitter(interval, 500)) defer t.Stop() + e.updateMarginAssetMaxBorrowable(ctx, marginService, market) for { select { case <-ctx.Done(): From dc0fca09f2329416c5e890e81976cccc843d0b85 Mon Sep 17 00:00:00 2001 From: c9s Date: Mon, 19 Sep 2022 09:28:28 +0800 Subject: [PATCH 02/15] types: rename json fields to grossProfit and grossLoss --- pkg/types/profit.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/types/profit.go b/pkg/types/profit.go index 0043271bd..716eb952d 100644 --- a/pkg/types/profit.go +++ b/pkg/types/profit.go @@ -154,15 +154,15 @@ type ProfitStats struct { AccumulatedPnL fixedpoint.Value `json:"accumulatedPnL,omitempty"` AccumulatedNetProfit fixedpoint.Value `json:"accumulatedNetProfit,omitempty"` - AccumulatedGrossProfit fixedpoint.Value `json:"accumulatedProfit,omitempty"` - AccumulatedGrossLoss fixedpoint.Value `json:"accumulatedLoss,omitempty"` + AccumulatedGrossProfit fixedpoint.Value `json:"accumulatedGrossProfit,omitempty"` + AccumulatedGrossLoss fixedpoint.Value `json:"accumulatedGrossLoss,omitempty"` AccumulatedVolume fixedpoint.Value `json:"accumulatedVolume,omitempty"` AccumulatedSince int64 `json:"accumulatedSince,omitempty"` TodayPnL fixedpoint.Value `json:"todayPnL,omitempty"` TodayNetProfit fixedpoint.Value `json:"todayNetProfit,omitempty"` - TodayGrossProfit fixedpoint.Value `json:"todayProfit,omitempty"` - TodayGrossLoss fixedpoint.Value `json:"todayLoss,omitempty"` + TodayGrossProfit fixedpoint.Value `json:"todayGrossProfit,omitempty"` + TodayGrossLoss fixedpoint.Value `json:"todayGrossLoss,omitempty"` TodaySince int64 `json:"todaySince,omitempty"` } From 26cf048c844112488cca7c3f7ab76f73dcd900cc Mon Sep 17 00:00:00 2001 From: c9s Date: Mon, 19 Sep 2022 09:31:04 +0800 Subject: [PATCH 03/15] types: preset fixedpoint zero fields --- pkg/types/profit.go | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/pkg/types/profit.go b/pkg/types/profit.go index 716eb952d..21c4ff188 100644 --- a/pkg/types/profit.go +++ b/pkg/types/profit.go @@ -168,10 +168,20 @@ type ProfitStats struct { func NewProfitStats(market Market) *ProfitStats { return &ProfitStats{ - Symbol: market.Symbol, - BaseCurrency: market.BaseCurrency, - QuoteCurrency: market.QuoteCurrency, - AccumulatedSince: time.Now().Unix(), + Symbol: market.Symbol, + QuoteCurrency: market.QuoteCurrency, + BaseCurrency: market.BaseCurrency, + AccumulatedPnL: fixedpoint.Zero, + AccumulatedNetProfit: fixedpoint.Zero, + AccumulatedGrossProfit: fixedpoint.Zero, + AccumulatedGrossLoss: fixedpoint.Zero, + AccumulatedVolume: fixedpoint.Zero, + AccumulatedSince: 0, + TodayPnL: fixedpoint.Zero, + TodayNetProfit: fixedpoint.Zero, + TodayGrossProfit: fixedpoint.Zero, + TodayGrossLoss: fixedpoint.Zero, + TodaySince: 0, } } From f9f2df29e7ca641ccb73cff9e1dff64f9e66fb71 Mon Sep 17 00:00:00 2001 From: c9s Date: Mon, 19 Sep 2022 09:32:26 +0800 Subject: [PATCH 04/15] types: use passed time to reset today pnl --- pkg/strategy/xmaker/state.go | 3 ++- pkg/types/profit.go | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/pkg/strategy/xmaker/state.go b/pkg/strategy/xmaker/state.go index 1b6169730..ec55d4101 100644 --- a/pkg/strategy/xmaker/state.go +++ b/pkg/strategy/xmaker/state.go @@ -2,6 +2,7 @@ package xmaker import ( "sync" + "time" "github.com/c9s/bbgo/pkg/fixedpoint" "github.com/c9s/bbgo/pkg/types" @@ -57,7 +58,7 @@ func (s *ProfitStats) AddTrade(trade types.Trade) { } func (s *ProfitStats) ResetToday() { - s.ProfitStats.ResetToday() + s.ProfitStats.ResetToday(time.Now()) s.lock.Lock() s.TodayMakerVolume = fixedpoint.Zero diff --git a/pkg/types/profit.go b/pkg/types/profit.go index 21c4ff188..121e4bc88 100644 --- a/pkg/types/profit.go +++ b/pkg/types/profit.go @@ -198,7 +198,7 @@ func (s *ProfitStats) Init(market Market) { func (s *ProfitStats) AddProfit(profit Profit) { if s.IsOver24Hours() { - s.ResetToday() + s.ResetToday(profit.TradedAt) } // since field guard @@ -227,7 +227,7 @@ func (s *ProfitStats) AddProfit(profit Profit) { func (s *ProfitStats) AddTrade(trade Trade) { if s.IsOver24Hours() { - s.ResetToday() + s.ResetToday(trade.Time.Time()) } s.AccumulatedVolume = s.AccumulatedVolume.Add(trade.Quantity) @@ -238,13 +238,13 @@ func (s *ProfitStats) IsOver24Hours() bool { return time.Since(time.Unix(s.TodaySince, 0)) >= 24*time.Hour } -func (s *ProfitStats) ResetToday() { +func (s *ProfitStats) ResetToday(t time.Time) { s.TodayPnL = fixedpoint.Zero s.TodayNetProfit = fixedpoint.Zero s.TodayGrossProfit = fixedpoint.Zero s.TodayGrossLoss = fixedpoint.Zero - var beginningOfTheDay = BeginningOfTheDay(time.Now().Local()) + var beginningOfTheDay = BeginningOfTheDay(t.Local()) s.TodaySince = beginningOfTheDay.Unix() } From 59b1e524391c32a1146179d33f20d97b089cd504 Mon Sep 17 00:00:00 2001 From: c9s Date: Mon, 19 Sep 2022 09:38:57 +0800 Subject: [PATCH 05/15] bbgo: remove submitOrder notification --- pkg/bbgo/order_execution.go | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/pkg/bbgo/order_execution.go b/pkg/bbgo/order_execution.go index faa6de109..677d1351f 100644 --- a/pkg/bbgo/order_execution.go +++ b/pkg/bbgo/order_execution.go @@ -109,18 +109,6 @@ type ExchangeOrderExecutor struct { orderUpdateCallbacks []func(order types.Order) } -func (e *ExchangeOrderExecutor) notifySubmitOrders(orders ...types.SubmitOrder) { - for _, order := range orders { - // pass submit order as an interface object. - channel, ok := e.RouteObject(&order) - if ok { - NotifyTo(channel, ":memo: Submitting %s %s %s order with quantity: %f @ %f, order: %v", order.Symbol, order.Type, order.Side, order.Quantity.Float64(), order.Price.Float64(), &order) - } else { - Notify(":memo: Submitting %s %s %s order with quantity: %f @ %f, order: %v", order.Symbol, order.Type, order.Side, order.Quantity.Float64(), order.Price.Float64(), &order) - } - } -} - func (e *ExchangeOrderExecutor) SubmitOrders(ctx context.Context, orders ...types.SubmitOrder) (types.OrderSlice, error) { formattedOrders, err := e.Session.FormatOrders(orders) if err != nil { @@ -128,19 +116,9 @@ func (e *ExchangeOrderExecutor) SubmitOrders(ctx context.Context, orders ...type } for _, order := range formattedOrders { - // pass submit order as an interface object. - channel, ok := e.RouteObject(&order) - if ok { - NotifyTo(channel, ":memo: Submitting %s %s %s order with quantity: %f, order: %v", order.Symbol, order.Type, order.Side, order.Quantity.Float64(), &order) - } else { - Notify(":memo: Submitting %s %s %s order with quantity: %f: %v", order.Symbol, order.Type, order.Side, order.Quantity.Float64(), &order) - } - log.Infof("submitting order: %s", order.String()) } - e.notifySubmitOrders(formattedOrders...) - createdOrders, _, err := BatchPlaceOrder(ctx, e.Session.Exchange, formattedOrders...) return createdOrders, err } From 850f3c86ba7a8e809706b3727b8ae91a0b340c86 Mon Sep 17 00:00:00 2001 From: c9s Date: Mon, 19 Sep 2022 09:44:26 +0800 Subject: [PATCH 06/15] types: fix net asset value display in telegram --- pkg/types/asset.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/pkg/types/asset.go b/pkg/types/asset.go index 8b17de422..c4865838b 100644 --- a/pkg/types/asset.go +++ b/pkg/types/asset.go @@ -59,25 +59,26 @@ func (m AssetMap) PlainText() (o string) { usd := a.InUSD btc := a.InBTC if !a.InUSD.IsZero() { - o += fmt.Sprintf(" %s: %s (≈ %s) (≈ %s)", + o += fmt.Sprintf(" %s: %s (≈ %s) (≈ %s)", a.Currency, - a.Total.String(), + a.NetAsset.String(), USD.FormatMoney(usd), BTC.FormatMoney(btc), ) + "\n" sumUsd = sumUsd.Add(usd) sumBTC = sumBTC.Add(btc) } else { - o += fmt.Sprintf(" %s: %s", + o += fmt.Sprintf(" %s: %s", a.Currency, - a.Total.String(), + a.NetAsset.String(), ) + "\n" } } - o += fmt.Sprintf(" Summary: (≈ %s) (≈ %s)", + + o += fmt.Sprintf("Net Asset Value: (≈ %s) (≈ %s)", USD.FormatMoney(sumUsd), BTC.FormatMoney(sumBTC), - ) + "\n" + ) return o } From e48ae215e5ee5f3bdc0c7df10a697bac7c280812 Mon Sep 17 00:00:00 2001 From: c9s Date: Mon, 19 Sep 2022 09:51:48 +0800 Subject: [PATCH 07/15] bbgo: remove Notifiability from the order executor --- pkg/bbgo/order_execution.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkg/bbgo/order_execution.go b/pkg/bbgo/order_execution.go index 677d1351f..1caab6b5b 100644 --- a/pkg/bbgo/order_execution.go +++ b/pkg/bbgo/order_execution.go @@ -98,8 +98,6 @@ func (e *ExchangeOrderExecutionRouter) CancelOrdersTo(ctx context.Context, sessi type ExchangeOrderExecutor struct { // MinQuoteBalance fixedpoint.Value `json:"minQuoteBalance,omitempty" yaml:"minQuoteBalance,omitempty"` - Notifiability `json:"-" yaml:"-"` - Session *ExchangeSession `json:"-" yaml:"-"` // private trade update callbacks From d4398bbbf9268611e9d89685976ddae0cc807cc8 Mon Sep 17 00:00:00 2001 From: c9s Date: Mon, 19 Sep 2022 13:07:56 +0800 Subject: [PATCH 08/15] bbgo: add more simple slice types to FilterSimpleArgs --- pkg/bbgo/order_executor_general.go | 2 +- pkg/util/simple_args.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/bbgo/order_executor_general.go b/pkg/bbgo/order_executor_general.go index 91eabedbf..7cca499cf 100644 --- a/pkg/bbgo/order_executor_general.go +++ b/pkg/bbgo/order_executor_general.go @@ -361,7 +361,7 @@ func (e *GeneralOrderExecutor) ClosePosition(ctx context.Context, percentage fix tagStr := strings.Join(tags, ",") submitOrder.Tag = tagStr - Notify("closing %s position %s with tags: %v", e.symbol, percentage.Percentage(), tagStr) + Notify("Closing %s position %s with tags: %v", e.symbol, percentage.Percentage(), tagStr) _, err := e.SubmitOrders(ctx, *submitOrder) return err diff --git a/pkg/util/simple_args.go b/pkg/util/simple_args.go index bef39d9e0..e71b8ca9c 100644 --- a/pkg/util/simple_args.go +++ b/pkg/util/simple_args.go @@ -12,7 +12,7 @@ import ( func FilterSimpleArgs(args []interface{}) (simpleArgs []interface{}) { for _, arg := range args { switch arg.(type) { - case int, int64, int32, uint64, uint32, string, []byte, float64, float32, fixedpoint.Value, time.Time: + case int, int64, int32, uint64, uint32, string, []string, []byte, float64, []float64, float32, fixedpoint.Value, time.Time: simpleArgs = append(simpleArgs, arg) default: rt := reflect.TypeOf(arg) From 05defc3aad90457bec746afcb230ea4f8f3ac425 Mon Sep 17 00:00:00 2001 From: c9s Date: Mon, 19 Sep 2022 13:12:49 +0800 Subject: [PATCH 09/15] bbgo: fix base amount borrow check --- pkg/bbgo/order_executor_general.go | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/pkg/bbgo/order_executor_general.go b/pkg/bbgo/order_executor_general.go index 7cca499cf..a70ed05c1 100644 --- a/pkg/bbgo/order_executor_general.go +++ b/pkg/bbgo/order_executor_general.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "strings" + "sync/atomic" "time" log "github.com/sirupsen/logrus" @@ -29,6 +30,8 @@ type GeneralOrderExecutor struct { tradeCollector *TradeCollector marginBaseMaxBorrowable, marginQuoteMaxBorrowable fixedpoint.Value + + closing int64 } func NewGeneralOrderExecutor(session *ExchangeSession, symbol, strategy, strategyInstanceID string, position *types.Position) *GeneralOrderExecutor { @@ -228,6 +231,11 @@ func (e *GeneralOrderExecutor) OpenPosition(ctx context.Context, options OpenPos Tag: strings.Join(options.Tags, ","), } + baseBalance, _ := e.session.Account.Balance(e.position.Market.BaseCurrency) + + // FIXME: fix the max quote borrowing checking + // quoteBalance, _ := e.session.Account.Balance(e.position.Market.QuoteCurrency) + if !options.LimitOrderTakerRatio.IsZero() { if options.Long { // use higher price to buy (this ensures that our order will be filled) @@ -258,7 +266,7 @@ func (e *GeneralOrderExecutor) OpenPosition(ctx context.Context, options OpenPos } quoteQuantity := quantity.Mul(price) - if quoteQuantity.Compare(e.marginQuoteMaxBorrowable) > 0 { + if !e.marginQuoteMaxBorrowable.IsZero() && quoteQuantity.Compare(e.marginQuoteMaxBorrowable) > 0 { log.Warnf("adjusting quantity %f according to the max margin quote borrowable amount: %f", quantity.Float64(), e.marginQuoteMaxBorrowable.Float64()) quantity = AdjustQuantityByMaxAmount(quantity, price, e.marginQuoteMaxBorrowable) } @@ -283,9 +291,10 @@ func (e *GeneralOrderExecutor) OpenPosition(ctx context.Context, options OpenPos } } - if quantity.Compare(e.marginBaseMaxBorrowable) > 0 { + if !e.marginBaseMaxBorrowable.IsZero() && quantity.Sub(baseBalance.Available).Compare(e.marginBaseMaxBorrowable) > 0 { log.Warnf("adjusting %f quantity according to the max margin base borrowable amount: %f", quantity.Float64(), e.marginBaseMaxBorrowable.Float64()) - quantity = fixedpoint.Min(quantity, e.marginBaseMaxBorrowable) + // quantity = fixedpoint.Min(quantity, e.marginBaseMaxBorrowable) + quantity = baseBalance.Available.Add(e.marginBaseMaxBorrowable) } submitOrder.Side = types.SideTypeSell @@ -348,6 +357,8 @@ func (e *GeneralOrderExecutor) ClosePosition(ctx context.Context, percentage fix return nil } + atomic.AddInt64(&e.closing, 1) + // check base balance and adjust the close position order if e.position.IsLong() { if baseBalance, ok := e.session.Account.Balance(e.position.Market.BaseCurrency); ok { From 1c23881da9f734fe83f97fa330039be842178e65 Mon Sep 17 00:00:00 2001 From: c9s Date: Mon, 19 Sep 2022 13:23:23 +0800 Subject: [PATCH 10/15] bbgo: check closing flag to avoid double closing --- pkg/bbgo/order_executor_general.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pkg/bbgo/order_executor_general.go b/pkg/bbgo/order_executor_general.go index a70ed05c1..456095822 100644 --- a/pkg/bbgo/order_executor_general.go +++ b/pkg/bbgo/order_executor_general.go @@ -357,7 +357,13 @@ func (e *GeneralOrderExecutor) ClosePosition(ctx context.Context, percentage fix return nil } + if e.closing > 0 { + log.Errorf("position is already closing") + return nil + } + atomic.AddInt64(&e.closing, 1) + defer atomic.StoreInt64(&e.closing, 0) // check base balance and adjust the close position order if e.position.IsLong() { @@ -367,6 +373,14 @@ func (e *GeneralOrderExecutor) ClosePosition(ctx context.Context, percentage fix if submitOrder.Quantity.IsZero() { return fmt.Errorf("insufficient base balance, can not sell: %+v", submitOrder) } + } else if e.position.IsShort() { + // TODO: check quote balance here, we also need the current price to validate, need to design. + /* + if quoteBalance, ok := e.session.Account.Balance(e.position.Market.QuoteCurrency); ok { + // AdjustQuantityByMaxAmount(submitOrder.Quantity, quoteBalance.Available) + // submitOrder.Quantity = fixedpoint.Min(submitOrder.Quantity,) + } + */ } tagStr := strings.Join(tags, ",") From 7ef008dc4f068901f85a1795350e3fd00135a6a7 Mon Sep 17 00:00:00 2001 From: c9s Date: Mon, 19 Sep 2022 14:55:58 +0800 Subject: [PATCH 11/15] telegramnotifier: show error message in the telegram log --- pkg/notifier/telegramnotifier/logrus_look.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/notifier/telegramnotifier/logrus_look.go b/pkg/notifier/telegramnotifier/logrus_look.go index eb93e9e20..8cfa229a4 100644 --- a/pkg/notifier/telegramnotifier/logrus_look.go +++ b/pkg/notifier/telegramnotifier/logrus_look.go @@ -34,6 +34,12 @@ func (t *LogHook) Fire(e *logrus.Entry) error { } var message = fmt.Sprintf("[%s] %s", e.Level.String(), e.Message) + if errData, ok := e.Data[logrus.ErrorKey]; ok { + if err, isErr := errData.(error); isErr { + message += " Error: " + err.Error() + } + } + t.notifier.Notify(message) return nil } From b3ae4929be18a8e5d52e8dd24ac8b95f7430dffd Mon Sep 17 00:00:00 2001 From: c9s Date: Mon, 19 Sep 2022 14:56:13 +0800 Subject: [PATCH 12/15] bbgo: make the max borrowing error message clear --- pkg/bbgo/order_executor_general.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/bbgo/order_executor_general.go b/pkg/bbgo/order_executor_general.go index 456095822..d465be1ef 100644 --- a/pkg/bbgo/order_executor_general.go +++ b/pkg/bbgo/order_executor_general.go @@ -72,7 +72,7 @@ func (e *GeneralOrderExecutor) startMarginAssetUpdater(ctx context.Context) { func (e *GeneralOrderExecutor) updateMarginAssetMaxBorrowable(ctx context.Context, marginService types.MarginBorrowRepayService, market types.Market) { maxBorrowable, err := marginService.QueryMarginAssetMaxBorrowable(ctx, market.BaseCurrency) if err != nil { - log.WithError(err).Errorf("can not query margin base asset max borrowable") + log.WithError(err).Errorf("can not query margin base asset %s max borrowable", market.BaseCurrency) } else { log.Infof("updating margin base asset %s max borrowable amount: %f", market.BaseCurrency, maxBorrowable.Float64()) e.marginBaseMaxBorrowable = maxBorrowable @@ -80,7 +80,7 @@ func (e *GeneralOrderExecutor) updateMarginAssetMaxBorrowable(ctx context.Contex maxBorrowable, err = marginService.QueryMarginAssetMaxBorrowable(ctx, market.QuoteCurrency) if err != nil { - log.WithError(err).Errorf("can not query margin base asset max borrowable") + log.WithError(err).Errorf("can not query margin quote asset %s max borrowable", market.QuoteCurrency) } else { log.Infof("updating margin quote asset %s max borrowable amount: %f", market.QuoteCurrency, maxBorrowable.Float64()) e.marginQuoteMaxBorrowable = maxBorrowable From c8f5bf8b088917f12d7ace42e5917cf963c0c26d Mon Sep 17 00:00:00 2001 From: c9s Date: Mon, 19 Sep 2022 16:00:12 +0800 Subject: [PATCH 13/15] bbgo: check e.session.Margin flag --- pkg/bbgo/order_executor_general.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/bbgo/order_executor_general.go b/pkg/bbgo/order_executor_general.go index d465be1ef..decf0dc0b 100644 --- a/pkg/bbgo/order_executor_general.go +++ b/pkg/bbgo/order_executor_general.go @@ -266,7 +266,7 @@ func (e *GeneralOrderExecutor) OpenPosition(ctx context.Context, options OpenPos } quoteQuantity := quantity.Mul(price) - if !e.marginQuoteMaxBorrowable.IsZero() && quoteQuantity.Compare(e.marginQuoteMaxBorrowable) > 0 { + if e.session.Margin && !e.marginQuoteMaxBorrowable.IsZero() && quoteQuantity.Compare(e.marginQuoteMaxBorrowable) > 0 { log.Warnf("adjusting quantity %f according to the max margin quote borrowable amount: %f", quantity.Float64(), e.marginQuoteMaxBorrowable.Float64()) quantity = AdjustQuantityByMaxAmount(quantity, price, e.marginQuoteMaxBorrowable) } @@ -291,7 +291,7 @@ func (e *GeneralOrderExecutor) OpenPosition(ctx context.Context, options OpenPos } } - if !e.marginBaseMaxBorrowable.IsZero() && quantity.Sub(baseBalance.Available).Compare(e.marginBaseMaxBorrowable) > 0 { + if e.session.Margin && !e.marginBaseMaxBorrowable.IsZero() && quantity.Sub(baseBalance.Available).Compare(e.marginBaseMaxBorrowable) > 0 { log.Warnf("adjusting %f quantity according to the max margin base borrowable amount: %f", quantity.Float64(), e.marginBaseMaxBorrowable.Float64()) // quantity = fixedpoint.Min(quantity, e.marginBaseMaxBorrowable) quantity = baseBalance.Available.Add(e.marginBaseMaxBorrowable) From d73880d0a8d776a3e5b8664a054ef23b729c9134 Mon Sep 17 00:00:00 2001 From: c9s Date: Mon, 19 Sep 2022 17:02:50 +0800 Subject: [PATCH 14/15] binance: upgrade github.com/adshao/go-binance/v2 to v2.3.8 --- go.mod | 6 +++--- go.sum | 6 ++++++ pkg/exchange/binance/exchange.go | 5 +++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 1700b335c..2531600ce 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ go 1.17 require ( github.com/DATA-DOG/go-sqlmock v1.5.0 github.com/Masterminds/squirrel v1.5.3 - github.com/adshao/go-binance/v2 v2.3.5 + github.com/adshao/go-binance/v2 v2.3.8 github.com/c-bata/goptuna v0.8.1 github.com/c9s/requestgen v1.3.0 github.com/c9s/rockhopper v1.2.2-0.20220617053729-ffdc87df194b @@ -92,7 +92,7 @@ require ( github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 // indirect - github.com/json-iterator/go v1.1.11 // indirect + github.com/json-iterator/go v1.1.12 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/leodido/go-urn v1.2.1 // indirect @@ -107,7 +107,7 @@ require ( github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/mitchellh/mapstructure v1.4.1 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.1 // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml v1.8.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect diff --git a/go.sum b/go.sum index ec684c4a9..c7221e919 100644 --- a/go.sum +++ b/go.sum @@ -51,6 +51,8 @@ github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdc github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= github.com/adshao/go-binance/v2 v2.3.5 h1:WVYZecm0w8l14YoWlnKZj6xxZT2AKMTHpMQSqIX1xxA= github.com/adshao/go-binance/v2 v2.3.5/go.mod h1:8Pg/FGTLyAhq8QXA0IkoReKyRpoxJcK3LVujKDAZV/c= +github.com/adshao/go-binance/v2 v2.3.8 h1:9VsAX4jUopnIOlzrvnKUFUf9SWB/nwPgJtUsM2dkj6A= +github.com/adshao/go-binance/v2 v2.3.8/go.mod h1:Z3MCnWI0gHC4Rea8TWiF3aN1t4nV9z3CaU/TeHcKsLM= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -401,6 +403,8 @@ github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= @@ -501,6 +505,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= github.com/muesli/clusters v0.0.0-20180605185049-a07a36e67d36 h1:KMCH+/bbZsAbFgzCXD3aB0DRZXnwAO8NYDmfIfslo+M= github.com/muesli/clusters v0.0.0-20180605185049-a07a36e67d36/go.mod h1:mw5KDqUj0eLj/6DUNINLVJNoPTFkEuGMHtJsXLviLkY= diff --git a/pkg/exchange/binance/exchange.go b/pkg/exchange/binance/exchange.go index 645c02bd0..04bf6c53f 100644 --- a/pkg/exchange/binance/exchange.go +++ b/pkg/exchange/binance/exchange.go @@ -312,6 +312,7 @@ func (e *Exchange) QueryMarginAssetMaxBorrowable(ctx context.Context, asset stri if e.IsIsolatedMargin { req.IsolatedSymbol(e.IsolatedMarginSymbol) } + resp, err := req.Do(ctx) if err != nil { return fixedpoint.Zero, err @@ -325,7 +326,7 @@ func (e *Exchange) RepayMarginAsset(ctx context.Context, asset string, amount fi req.Asset(asset) req.Amount(amount.String()) if e.IsIsolatedMargin { - req.IsolatedSymbol(e.IsolatedMarginSymbol) + req.Symbol(e.IsolatedMarginSymbol) } log.Infof("repaying margin asset %s amount %f", asset, amount.Float64()) @@ -343,7 +344,7 @@ func (e *Exchange) BorrowMarginAsset(ctx context.Context, asset string, amount f req.Asset(asset) req.Amount(amount.String()) if e.IsIsolatedMargin { - req.IsolatedSymbol(e.IsolatedMarginSymbol) + req.Symbol(e.IsolatedMarginSymbol) } log.Infof("borrowing margin asset %s amount %f", asset, amount.Float64()) From 1c58a44e4472eeb081c8eb12e10c925b7249b15a Mon Sep 17 00:00:00 2001 From: c9s Date: Mon, 19 Sep 2022 17:09:34 +0800 Subject: [PATCH 15/15] binance: implement get margin max borrowable request --- .../get_margin_max_borrowable_request.go | 25 +++ ...argin_max_borrowable_request_requestgen.go | 161 ++++++++++++++++++ pkg/exchange/binance/exchange.go | 4 +- 3 files changed, 188 insertions(+), 2 deletions(-) create mode 100644 pkg/exchange/binance/binanceapi/get_margin_max_borrowable_request.go create mode 100644 pkg/exchange/binance/binanceapi/get_margin_max_borrowable_request_requestgen.go diff --git a/pkg/exchange/binance/binanceapi/get_margin_max_borrowable_request.go b/pkg/exchange/binance/binanceapi/get_margin_max_borrowable_request.go new file mode 100644 index 000000000..31e307ac5 --- /dev/null +++ b/pkg/exchange/binance/binanceapi/get_margin_max_borrowable_request.go @@ -0,0 +1,25 @@ +package binanceapi + +import ( + "github.com/c9s/requestgen" + + "github.com/c9s/bbgo/pkg/fixedpoint" +) + +// MarginMaxBorrowable is the user margin interest record +type MarginMaxBorrowable struct { + Amount fixedpoint.Value `json:"amount"` + BorrowLimit fixedpoint.Value `json:"borrowLimit"` +} + +//go:generate requestgen -method GET -url "/sapi/v1/margin/maxBorrowable" -type GetMarginMaxBorrowableRequest -responseType .MarginMaxBorrowable +type GetMarginMaxBorrowableRequest struct { + client requestgen.AuthenticatedAPIClient + + asset string `param:"asset"` + isolatedSymbol *string `param:"isolatedSymbol"` +} + +func (c *RestClient) NewGetMarginMaxBorrowableRequest() *GetMarginMaxBorrowableRequest { + return &GetMarginMaxBorrowableRequest{client: c} +} diff --git a/pkg/exchange/binance/binanceapi/get_margin_max_borrowable_request_requestgen.go b/pkg/exchange/binance/binanceapi/get_margin_max_borrowable_request_requestgen.go new file mode 100644 index 000000000..a4b329864 --- /dev/null +++ b/pkg/exchange/binance/binanceapi/get_margin_max_borrowable_request_requestgen.go @@ -0,0 +1,161 @@ +// Code generated by "requestgen -method GET -url /sapi/v1/margin/maxBorrowable -type GetMarginMaxBorrowableRequest -responseType .MarginMaxBorrowable"; DO NOT EDIT. + +package binanceapi + +import ( + "context" + "encoding/json" + "fmt" + "net/url" + "reflect" + "regexp" +) + +func (g *GetMarginMaxBorrowableRequest) Asset(asset string) *GetMarginMaxBorrowableRequest { + g.asset = asset + return g +} + +func (g *GetMarginMaxBorrowableRequest) IsolatedSymbol(isolatedSymbol string) *GetMarginMaxBorrowableRequest { + g.isolatedSymbol = &isolatedSymbol + return g +} + +// GetQueryParameters builds and checks the query parameters and returns url.Values +func (g *GetMarginMaxBorrowableRequest) GetQueryParameters() (url.Values, error) { + var params = map[string]interface{}{} + + query := url.Values{} + for _k, _v := range params { + query.Add(_k, fmt.Sprintf("%v", _v)) + } + + return query, nil +} + +// GetParameters builds and checks the parameters and return the result in a map object +func (g *GetMarginMaxBorrowableRequest) GetParameters() (map[string]interface{}, error) { + var params = map[string]interface{}{} + // check asset field -> json key asset + asset := g.asset + + // assign parameter of asset + params["asset"] = asset + // check isolatedSymbol field -> json key isolatedSymbol + if g.isolatedSymbol != nil { + isolatedSymbol := *g.isolatedSymbol + + // assign parameter of isolatedSymbol + params["isolatedSymbol"] = isolatedSymbol + } else { + } + + return params, nil +} + +// GetParametersQuery converts the parameters from GetParameters into the url.Values format +func (g *GetMarginMaxBorrowableRequest) GetParametersQuery() (url.Values, error) { + query := url.Values{} + + params, err := g.GetParameters() + if err != nil { + return query, err + } + + for _k, _v := range params { + if g.isVarSlice(_v) { + g.iterateSlice(_v, func(it interface{}) { + query.Add(_k+"[]", fmt.Sprintf("%v", it)) + }) + } else { + query.Add(_k, fmt.Sprintf("%v", _v)) + } + } + + return query, nil +} + +// GetParametersJSON converts the parameters from GetParameters into the JSON format +func (g *GetMarginMaxBorrowableRequest) GetParametersJSON() ([]byte, error) { + params, err := g.GetParameters() + if err != nil { + return nil, err + } + + return json.Marshal(params) +} + +// GetSlugParameters builds and checks the slug parameters and return the result in a map object +func (g *GetMarginMaxBorrowableRequest) GetSlugParameters() (map[string]interface{}, error) { + var params = map[string]interface{}{} + + return params, nil +} + +func (g *GetMarginMaxBorrowableRequest) applySlugsToUrl(url string, slugs map[string]string) string { + for _k, _v := range slugs { + needleRE := regexp.MustCompile(":" + _k + "\\b") + url = needleRE.ReplaceAllString(url, _v) + } + + return url +} + +func (g *GetMarginMaxBorrowableRequest) iterateSlice(slice interface{}, _f func(it interface{})) { + sliceValue := reflect.ValueOf(slice) + for _i := 0; _i < sliceValue.Len(); _i++ { + it := sliceValue.Index(_i).Interface() + _f(it) + } +} + +func (g *GetMarginMaxBorrowableRequest) isVarSlice(_v interface{}) bool { + rt := reflect.TypeOf(_v) + switch rt.Kind() { + case reflect.Slice: + return true + } + return false +} + +func (g *GetMarginMaxBorrowableRequest) GetSlugsMap() (map[string]string, error) { + slugs := map[string]string{} + params, err := g.GetSlugParameters() + if err != nil { + return slugs, nil + } + + for _k, _v := range params { + slugs[_k] = fmt.Sprintf("%v", _v) + } + + return slugs, nil +} + +func (g *GetMarginMaxBorrowableRequest) Do(ctx context.Context) (*MarginMaxBorrowable, error) { + + // empty params for GET operation + var params interface{} + query, err := g.GetParametersQuery() + if err != nil { + return nil, err + } + + apiURL := "/sapi/v1/margin/maxBorrowable" + + req, err := g.client.NewAuthenticatedRequest(ctx, "GET", apiURL, query, params) + if err != nil { + return nil, err + } + + response, err := g.client.SendRequest(req) + if err != nil { + return nil, err + } + + var apiResponse MarginMaxBorrowable + if err := response.DecodeJSON(&apiResponse); err != nil { + return nil, err + } + return &apiResponse, nil +} diff --git a/pkg/exchange/binance/exchange.go b/pkg/exchange/binance/exchange.go index 04bf6c53f..0ef2fcd9a 100644 --- a/pkg/exchange/binance/exchange.go +++ b/pkg/exchange/binance/exchange.go @@ -307,7 +307,7 @@ func (e *Exchange) NewStream() types.Stream { } func (e *Exchange) QueryMarginAssetMaxBorrowable(ctx context.Context, asset string) (amount fixedpoint.Value, err error) { - req := e.client.NewGetMaxBorrowableService() + req := e.client2.NewGetMarginMaxBorrowableRequest() req.Asset(asset) if e.IsIsolatedMargin { req.IsolatedSymbol(e.IsolatedMarginSymbol) @@ -318,7 +318,7 @@ func (e *Exchange) QueryMarginAssetMaxBorrowable(ctx context.Context, asset stri return fixedpoint.Zero, err } - return fixedpoint.NewFromString(resp.Amount) + return resp.Amount, nil } func (e *Exchange) RepayMarginAsset(ctx context.Context, asset string, amount fixedpoint.Value) error {