mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-26 00:35:15 +00:00
Merge pull request #958 from c9s/strategy/pivotshort
WIP: strategy/pivotshort: more improvements
This commit is contained in:
commit
29376defa3
6
go.mod
6
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
|
||||
|
|
6
go.sum
6
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=
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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())
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue
Block a user