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/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 diff --git a/pkg/bbgo/order_executor_general.go b/pkg/bbgo/order_executor_general.go index 0b8f60f67..decf0dc0b 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 { @@ -69,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 @@ -77,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 @@ -88,6 +91,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(): @@ -227,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) @@ -257,7 +266,7 @@ func (e *GeneralOrderExecutor) OpenPosition(ctx context.Context, options OpenPos } quoteQuantity := quantity.Mul(price) - if 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) } @@ -282,9 +291,10 @@ func (e *GeneralOrderExecutor) OpenPosition(ctx context.Context, options OpenPos } } - if quantity.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 = fixedpoint.Min(quantity, e.marginBaseMaxBorrowable) + quantity = baseBalance.Available.Add(e.marginBaseMaxBorrowable) } submitOrder.Side = types.SideTypeSell @@ -347,6 +357,14 @@ 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() { if baseBalance, ok := e.session.Account.Balance(e.position.Market.BaseCurrency); ok { @@ -355,12 +373,20 @@ 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, ",") 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/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 645c02bd0..0ef2fcd9a 100644 --- a/pkg/exchange/binance/exchange.go +++ b/pkg/exchange/binance/exchange.go @@ -307,17 +307,18 @@ 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) } + resp, err := req.Do(ctx) if err != nil { 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 { @@ -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()) 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 } 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 0043271bd..121e4bc88 100644 --- a/pkg/types/profit.go +++ b/pkg/types/profit.go @@ -154,24 +154,34 @@ 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"` } 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, } } @@ -188,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 @@ -217,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) @@ -228,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() } 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)